[gimp] pdb: git didn't commit the "groups" subdirectory...



commit f4ce55a31a60210bd0e8af59b7d41c3d38d3b59e
Author: Michael Natterer <mitch gimp org>
Date:   Sun Dec 17 19:03:18 2017 +0100

    pdb: git didn't commit the "groups" subdirectory...

 pdb/groups/brush.pdb               |  883 +++++++
 pdb/groups/brush_select.pdb        |  144 ++
 pdb/groups/brushes.pdb             |  227 ++
 pdb/groups/buffer.pdb              |  260 ++
 pdb/groups/channel.pdb             |  382 +++
 pdb/groups/color.pdb               |  759 ++++++
 pdb/groups/context.pdb             | 3111 ++++++++++++++++++++++
 pdb/groups/debug.pdb               |  104 +
 pdb/groups/display.pdb             |  229 ++
 pdb/groups/drawable.pdb            | 1035 ++++++++
 pdb/groups/drawable_color.pdb      |  838 ++++++
 pdb/groups/drawable_transform.pdb  |  934 +++++++
 pdb/groups/dynamics.pdb            |   87 +
 pdb/groups/edit.pdb                | 1052 ++++++++
 pdb/groups/fileops.pdb             |  720 ++++++
 pdb/groups/floating_sel.pdb        |  222 ++
 pdb/groups/font_select.pdb         |  111 +
 pdb/groups/fonts.pdb               |   86 +
 pdb/groups/gimp.pdb                |  244 ++
 pdb/groups/gimprc.pdb              |  284 ++
 pdb/groups/gradient.pdb            | 1466 +++++++++++
 pdb/groups/gradient_select.pdb     |  123 +
 pdb/groups/gradients.pdb           |  268 ++
 pdb/groups/help.pdb                |   72 +
 pdb/groups/image.pdb               | 2929 +++++++++++++++++++++
 pdb/groups/image_color_profile.pdb |  327 +++
 pdb/groups/image_convert.pdb       |  286 +++
 pdb/groups/image_grid.pdb          |  372 +++
 pdb/groups/image_guides.pdb        |  270 ++
 pdb/groups/image_sample_points.pdb |  202 ++
 pdb/groups/image_select.pdb        |  438 ++++
 pdb/groups/image_transform.pdb     |  276 ++
 pdb/groups/image_undo.pdb          |  306 +++
 pdb/groups/item.pdb                |  992 +++++++
 pdb/groups/item_transform.pdb      |  695 +++++
 pdb/groups/layer.pdb               | 1406 ++++++++++
 pdb/groups/message.pdb             |  116 +
 pdb/groups/paint_tools.pdb         | 1071 ++++++++
 pdb/groups/palette.pdb             |  587 +++++
 pdb/groups/palette_select.pdb      |  116 +
 pdb/groups/palettes.pdb            |  167 ++
 pdb/groups/paths.pdb               |  636 +++++
 pdb/groups/pattern.pdb             |  130 +
 pdb/groups/pattern_select.pdb      |  116 +
 pdb/groups/patterns.pdb            |  171 ++
 pdb/groups/plug_in.pdb             |  449 ++++
 pdb/groups/plug_in_compat.pdb      | 4966 ++++++++++++++++++++++++++++++++++++
 pdb/groups/procedural_db.pdb       |  521 ++++
 pdb/groups/progress.pdb            |  321 +++
 pdb/groups/selection.pdb           |  578 +++++
 pdb/groups/selection_tools.pdb     |  419 +++
 pdb/groups/text_layer.pdb          | 1253 +++++++++
 pdb/groups/text_tool.pdb           |  288 +++
 pdb/groups/transform_tools.pdb     |  532 ++++
 pdb/groups/unit.pdb                |  376 +++
 pdb/groups/vectors.pdb             | 1365 ++++++++++
 56 files changed, 36318 insertions(+), 0 deletions(-)
---
diff --git a/pdb/groups/brush.pdb b/pdb/groups/brush.pdb
new file mode 100644
index 0000000..4fe01e1
--- /dev/null
+++ b/pdb/groups/brush.pdb
@@ -0,0 +1,883 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub brush_new {
+    $blurb = "Creates a new brush.";
+    $help  = "This procedure creates a new, uninitialized brush.";
+
+    &mitch_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The requested name of the new brush' }
+    );
+
+    @outargs = (
+       { name => 'actual_name', type => 'string',
+         desc => 'The actual new brush name. The returned value must be freed with g_free()' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpData *data = gimp_data_factory_data_new (gimp->brush_factory,
+                                               context, name);
+
+  if (data)
+    actual_name = g_strdup (gimp_object_get_name (data));
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub brush_duplicate {
+    $blurb = "Duplicates a brush.";
+    $help  = "This procedure creates an identical brush by a different name.";
+
+    &mitch_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The brush name' }
+    );
+
+    @outargs = (
+       { name => 'copy_name', type => 'string',
+         desc => "The name of the brush's copy. The returned value must be freed with g_free()" }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpBrush *brush = gimp_pdb_get_brush (gimp, name, GIMP_PDB_DATA_ACCESS_READ, error);
+
+  if (brush)
+    {
+      GimpBrush *brush_copy = (GimpBrush *)
+        gimp_data_factory_data_duplicate (gimp->brush_factory,
+                                          GIMP_DATA (brush));
+
+      if (brush_copy)
+        copy_name = g_strdup (gimp_object_get_name (brush_copy));
+      else
+        success = FALSE;
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub brush_is_generated {
+    $blurb = "Tests if brush is generated.";
+    $help  = "Returns TRUE if this brush is parametric, FALSE for other types.";
+
+    &bill_pdb_misc('2004', '2.4');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The brush name' }
+    );
+
+    @outargs = (
+       { name => 'generated', type => 'boolean',
+         desc => 'TRUE if the brush is generated' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpBrush *brush = gimp_pdb_get_brush (gimp, name, GIMP_PDB_DATA_ACCESS_READ, error);
+
+  if (brush)
+    generated = GIMP_IS_BRUSH_GENERATED (brush);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub brush_is_editable {
+    $blurb = "Tests if brush can be edited.";
+    $help  = "Returns TRUE if you have permission to change the brush.";
+
+    &bill_pdb_misc('2004', '2.4');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The brush name' }
+    );
+
+    @outargs = (
+       { name => 'editable', type => 'boolean',
+         desc => 'TRUE if the brush can be edited' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpBrush *brush = gimp_pdb_get_brush (gimp, name, GIMP_PDB_DATA_ACCESS_READ, error);
+
+  if (brush)
+    editable = gimp_data_is_writable (GIMP_DATA (brush));
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub brush_rename {
+    $blurb = "Renames a brush.";
+    $help  = "This procedure renames a brush.";
+
+    &mitch_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The brush name' },
+        { name => 'new_name', type => 'string', non_empty => 1,
+          desc => 'The new name of the brush' }
+    );
+
+    @outargs = (
+       { name => 'actual_name', type => 'string',
+         desc => 'The actual new name of the brush. The returned value must be freed with g_free()' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpBrush *brush = gimp_pdb_get_brush (gimp, name, GIMP_PDB_DATA_ACCESS_RENAME, error);
+
+  if (brush)
+    {
+      gimp_object_set_name (GIMP_OBJECT (brush), new_name);
+      actual_name = g_strdup (gimp_object_get_name (brush));
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub brush_delete {
+    $blurb = "Deletes a brush.";
+    $help  = "This procedure deletes a brush.";
+
+    &mitch_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The brush name' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpBrush *brush = gimp_pdb_get_brush (gimp, name, GIMP_PDB_DATA_ACCESS_READ, error);
+
+  if (brush && gimp_data_is_deletable (GIMP_DATA (brush)))
+    success = gimp_data_factory_data_delete (gimp->brush_factory,
+                                             GIMP_DATA (brush),
+                                             TRUE, error);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub brush_get_info {
+    $blurb = "Retrieves information about the specified brush.";
+
+    $help = <<'HELP';
+This procedure retrieves information about the specified brush:
+brush extents (width and height), color depth and mask depth.
+HELP
+
+    &mitch_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The brush name' }
+    );
+
+    @outargs = (
+       { name => 'width', type => 'int32', void_ret => 1,
+          desc => 'The brush width' },
+       { name => 'height', type => 'int32',
+          desc => 'The brush height' },
+       { name => 'mask_bpp', type => 'int32',
+          desc => 'The brush mask bpp' },
+       { name => 'color_bpp', type => 'int32',
+          desc => 'The brush color bpp' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpBrush *brush = gimp_pdb_get_brush (gimp, name, GIMP_PDB_DATA_ACCESS_READ, error);
+
+  if (brush)
+    {
+      GimpTempBuf *mask   = gimp_brush_get_mask (brush);
+      GimpTempBuf *pixmap = gimp_brush_get_pixmap (brush);
+
+      width     = gimp_brush_get_width  (brush);
+      height    = gimp_brush_get_height (brush);
+      mask_bpp  = babl_format_get_bytes_per_pixel (gimp_temp_buf_get_format (mask));
+      color_bpp = pixmap ? babl_format_get_bytes_per_pixel (gimp_temp_buf_get_format (pixmap)) : 0;
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub brush_get_pixels {
+    $blurb = 'Retrieves information about the specified brush.';
+
+    $help = <<'HELP';
+This procedure retrieves information about the specified brush. This
+includes the brush extents (width and height) and its pixels data.
+HELP
+
+    &mitch_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The brush name' }
+    );
+
+    @outargs = (
+       { name => 'width', type => 'int32', void_ret => 1,
+          desc => 'The brush width' },
+       { name => 'height', type => 'int32',
+          desc => 'The brush height' },
+       { name => 'mask_bpp', type => 'int32',
+          desc => 'The brush mask bpp' },
+        { name => 'mask_bytes', type => 'int8array',
+         desc => 'The brush mask data',
+          array => { desc => 'Length of brush mask data' } },
+       { name => 'color_bpp', type => 'int32',
+          desc => 'The brush color bpp' },
+        { name => 'color_bytes', type => 'int8array',
+         desc => 'The brush color data',
+          array => { desc => 'Length of brush color data' } }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpBrush *brush = gimp_pdb_get_brush (gimp, name, GIMP_PDB_DATA_ACCESS_READ, error);
+
+  if (brush)
+    {
+      GimpTempBuf *mask   = gimp_brush_get_mask (brush);
+      GimpTempBuf *pixmap = gimp_brush_get_pixmap (brush);
+
+      width          = gimp_temp_buf_get_width  (mask);
+      height         = gimp_temp_buf_get_height (mask);
+      mask_bpp       = babl_format_get_bytes_per_pixel (gimp_temp_buf_get_format (mask));
+      num_mask_bytes = gimp_temp_buf_get_height (mask) *
+                       gimp_temp_buf_get_width  (mask) * mask_bpp;
+      mask_bytes     = g_memdup (gimp_temp_buf_get_data (mask),
+                                 num_mask_bytes);
+
+      if (pixmap)
+        {
+          color_bpp       = babl_format_get_bytes_per_pixel (gimp_temp_buf_get_format (pixmap));
+          num_color_bytes = gimp_temp_buf_get_height (pixmap) *
+                            gimp_temp_buf_get_width  (pixmap) *
+                            color_bpp;
+          color_bytes     = g_memdup (gimp_temp_buf_get_data (pixmap),
+                                      num_color_bytes);
+        }
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub brush_get_spacing {
+    $blurb = 'Gets the brush spacing.';
+
+    $help = <<'HELP';
+This procedure returns the spacing setting for the specified brush.
+The return value is an integer between 0 and 1000 which represents
+percentage of the maximum of the width and height of the mask.
+HELP
+
+    &mitch_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The brush name' }
+    );
+
+    @outargs = (
+       { name => 'spacing', type => '0 <= int32 <= 1000', void_ret => 1,
+         desc => 'The brush spacing' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpBrush *brush = gimp_pdb_get_brush (gimp, name, GIMP_PDB_DATA_ACCESS_READ, error);
+
+  if (brush)
+    spacing = gimp_brush_get_spacing (brush);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub brush_get_shape {
+    $blurb = 'Gets the shape of a generated brush.';
+
+    $help = <<'HELP';
+This procedure gets the shape value for a generated brush. If
+called for any other type of brush, it does not succeed.  The
+current possibilities are Circle (GIMP_BRUSH_GENERATED_CIRCLE),
+Square (GIMP_BRUSH_GENERATED_SQUARE), and Diamond
+(GIMP_BRUSH_GENERATED_DIAMOND).  Other shapes are likely to be
+added in the future. 
+HELP
+
+    &bill_pdb_misc('2004', '2.4');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The brush name' }
+    );
+
+    @outargs = (
+       { name => 'shape', type => 'enum GimpBrushGeneratedShape',
+          desc => 'The brush shape' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpBrush *brush = gimp_pdb_get_generated_brush (gimp, name, GIMP_PDB_DATA_ACCESS_READ, error);
+
+  if (brush)
+    shape = GIMP_BRUSH_GENERATED (brush)->shape;
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub brush_get_radius {
+    $blurb = 'Gets the radius of a generated brush.';
+
+    $help = <<'HELP';
+This procedure gets the radius value for a generated brush. If
+called for any other type of brush, it does not succeed.
+HELP
+
+    &bill_pdb_misc('2004', '2.4');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The brush name' }
+    );
+
+    @outargs = (
+       { name => 'radius', type => 'float',
+          desc => 'The radius of the brush in pixels' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpBrush *brush = gimp_pdb_get_generated_brush (gimp, name, GIMP_PDB_DATA_ACCESS_READ, error);
+
+  if (brush)
+    radius = GIMP_BRUSH_GENERATED (brush)->radius;
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub brush_get_spikes {
+    $blurb = 'Gets the number of spikes for a generated brush.';
+
+    $help = <<'HELP';
+This procedure gets the number of spikes for a generated brush.  
+If called for any other type of brush, it does not succeed.
+HELP
+
+    &bill_pdb_misc('2004', '2.4');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The brush name' }
+    );
+
+    @outargs = (
+       { name => 'spikes', type => 'int32',
+          desc => 'The number of spikes on the brush.' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpBrush *brush = gimp_pdb_get_generated_brush (gimp, name, GIMP_PDB_DATA_ACCESS_READ, error);
+
+  if (brush)
+    spikes = GIMP_BRUSH_GENERATED (brush)->spikes;
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub brush_get_hardness {
+    $blurb = 'Gets the hardness of a generated brush.';
+
+    $help = <<'HELP';
+This procedure gets the hardness of a generated brush. The
+hardness of a brush is the amount its intensity fades at the
+outside edge, as a float between 0.0 and 1.0.
+If called for any other type of brush, the function does not
+succeed.
+HELP
+
+    &bill_pdb_misc('2004', '2.4');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The brush name' }
+    );
+
+    @outargs = (
+       { name => 'hardness', type => 'float',
+          desc => 'The hardness of the brush.' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpBrush *brush = gimp_pdb_get_generated_brush (gimp, name, GIMP_PDB_DATA_ACCESS_READ, error);
+
+  if (brush)
+    hardness = GIMP_BRUSH_GENERATED (brush)->hardness;
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub brush_get_aspect_ratio {
+    $blurb = 'Gets the aspect ratio of a generated brush.';
+
+    $help = <<'HELP';
+This procedure gets the aspect ratio of a generated brush. 
+If called for any other type of brush, it does not succeed.
+The return value is a float between 0.0 and 1000.0.
+HELP
+
+    &bill_pdb_misc('2004', '2.4');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The brush name' }
+    );
+
+    @outargs = (
+       { name => 'aspect_ratio', type => 'float',
+          desc => 'The aspect ratio of the brush.' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpBrush *brush = gimp_pdb_get_generated_brush (gimp, name, GIMP_PDB_DATA_ACCESS_READ, error);
+
+  if (brush)
+    aspect_ratio = GIMP_BRUSH_GENERATED (brush)->aspect_ratio;
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub brush_get_angle {
+    $blurb = 'Gets the rotation angle of a generated brush.';
+
+    $help = <<'HELP';
+This procedure gets the angle of rotation for a generated brush. 
+If called for any other type of brush, it does not succeed.
+HELP
+
+    &bill_pdb_misc('2004', '2.4');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The brush name' }
+    );
+
+    @outargs = (
+       { name => 'angle', type => 'float',
+          desc => 'The rotation angle of the brush in degree.' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpBrush *brush = gimp_pdb_get_generated_brush (gimp, name, GIMP_PDB_DATA_ACCESS_READ, error);
+
+  if (brush)
+    angle = GIMP_BRUSH_GENERATED (brush)->angle;
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub brush_set_spacing {
+    $blurb = 'Sets the brush spacing.';
+
+    $help = <<'HELP';
+This procedure modifies the spacing setting for the specified brush.
+The value should be a integer between 0 and 1000.
+HELP
+
+    &bill_pdb_misc('2004', '2.4');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The brush name' },
+       { name => 'spacing', type => '0 <= int32 <= 1000',
+         desc => 'The brush spacing' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpBrush *brush = gimp_pdb_get_brush (gimp, name, GIMP_PDB_DATA_ACCESS_WRITE, error);
+
+  if (brush)
+    gimp_brush_set_spacing (brush, spacing);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub brush_set_shape {
+    $blurb = 'Sets the shape of a generated brush.';
+
+    $help = <<'HELP';
+This procedure sets the shape value for a generated brush. If
+called for any other type of brush, it does not succeed. The
+current possibilities are Circle (GIMP_BRUSH_GENERATED_CIRCLE),
+Square (GIMP_BRUSH_GENERATED_SQUARE), and Diamond
+(GIMP_BRUSH_GENERATED_DIAMOND). Other shapes are likely to be
+added in the future. 
+HELP
+
+    &bill_pdb_misc('2004', '2.4');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The brush name' },
+       { name => 'shape_in', type => 'enum GimpBrushGeneratedShape',
+          desc => 'The brush shape' }
+    );
+
+    @outargs = (
+       { name => 'shape_out', type => 'enum GimpBrushGeneratedShape',
+          desc => 'The brush shape actually assigned' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpBrush *brush = gimp_pdb_get_generated_brush (gimp, name, GIMP_PDB_DATA_ACCESS_WRITE, error);
+
+  if (brush)
+    {
+      gimp_brush_generated_set_shape (GIMP_BRUSH_GENERATED (brush),
+                                      shape_in);
+      shape_out = GIMP_BRUSH_GENERATED (brush)->shape;
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub brush_set_radius {
+    $blurb = 'Sets the radius of a generated brush.';
+
+    $help = <<'HELP';
+This procedure sets the radius for a generated brush. If
+called for any other type of brush, it does not succeed.
+HELP
+
+    &bill_pdb_misc('2004', '2.4');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The brush name' },
+       { name => 'radius_in', type => 'float',
+          desc => 'The desired brush radius in pixel' }
+    );
+
+    @outargs = (
+       { name => 'radius_out', type => 'float',
+          desc => 'The brush radius actually assigned' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpBrush *brush = gimp_pdb_get_generated_brush (gimp, name, GIMP_PDB_DATA_ACCESS_WRITE, error);
+
+  if (brush)
+    {
+      gimp_brush_generated_set_radius (GIMP_BRUSH_GENERATED (brush),
+                                       radius_in);
+      radius_out = GIMP_BRUSH_GENERATED (brush)->radius;
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub brush_set_spikes {
+    $blurb = 'Sets the number of spikes for a generated brush.';
+
+    $help = <<'HELP';
+This procedure sets the number of spikes for a generated brush. If
+called for any other type of brush, it does not succeed.
+HELP
+
+    &bill_pdb_misc('2004', '2.4');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The brush name' },
+       { name => 'spikes_in', type => 'int32',
+          desc => 'The desired number of spikes' }
+    );
+
+    @outargs = (
+       { name => 'spikes_out', type => 'int32',
+          desc => 'The number of spikes actually assigned' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpBrush *brush = gimp_pdb_get_generated_brush (gimp, name, GIMP_PDB_DATA_ACCESS_WRITE, error);
+
+  if (brush)
+    {
+      gimp_brush_generated_set_spikes (GIMP_BRUSH_GENERATED (brush),
+                                       spikes_in);
+      spikes_out = GIMP_BRUSH_GENERATED (brush)->spikes;
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub brush_set_hardness {
+    $blurb = 'Sets the hardness of a generated brush.';
+
+    $help = <<'HELP';
+This procedure sets the hardness for a generated brush. If
+called for any other type of brush, it does not succeed.
+The value should be a float between 0.0 and 1.0.
+HELP
+
+    &bill_pdb_misc('2004', '2.4');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The brush name' },
+       { name => 'hardness_in', type => 'float',
+          desc => 'The desired brush hardness' }
+    );
+
+    @outargs = (
+       { name => 'hardness_out', type => 'float',
+          desc => 'The brush hardness actually assigned' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpBrush *brush = gimp_pdb_get_generated_brush (gimp, name, GIMP_PDB_DATA_ACCESS_WRITE, error);
+
+  if (brush)
+    {
+      gimp_brush_generated_set_hardness (GIMP_BRUSH_GENERATED (brush),
+                                         hardness_in);
+      hardness_out = GIMP_BRUSH_GENERATED (brush)->hardness;
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub brush_set_aspect_ratio {
+    $blurb = 'Sets the aspect ratio of a generated brush.';
+
+    $help = <<'HELP';
+This procedure sets the aspect ratio for a generated brush.
+If called for any other type of brush, it does not succeed.
+The value should be a float between 0.0 and 1000.0.
+HELP
+
+    &bill_pdb_misc('2004', '2.4');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The brush name' },
+       { name => 'aspect_ratio_in', type => 'float',
+          desc => 'The desired brush aspect ratio' }
+    );
+
+    @outargs = (
+       { name => 'aspect_ratio_out', type => 'float',
+          desc => 'The brush aspect ratio actually assigned' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpBrush *brush = gimp_pdb_get_generated_brush (gimp, name, GIMP_PDB_DATA_ACCESS_WRITE, error);
+
+  if (brush)
+    {
+      gimp_brush_generated_set_aspect_ratio (GIMP_BRUSH_GENERATED (brush),
+                                             aspect_ratio_in);
+      aspect_ratio_out = GIMP_BRUSH_GENERATED (brush)->aspect_ratio;
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub brush_set_angle {
+    $blurb = 'Sets the rotation angle of a generated brush.';
+
+    $help = <<'HELP';
+This procedure sets the rotation angle for a generated brush. If
+called for any other type of brush, it does not succeed.
+HELP
+
+    &bill_pdb_misc('2004', '2.4');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The brush name' },
+       { name => 'angle_in', type => 'float',
+          desc => 'The desired brush rotation angle in degree' }
+    );
+
+    @outargs = (
+       { name => 'angle_out', type => 'float',
+          desc => 'The brush rotation angle actually assigned' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpBrush *brush = gimp_pdb_get_generated_brush (gimp, name, GIMP_PDB_DATA_ACCESS_WRITE, error);
+
+  if (brush)
+    {
+      gimp_brush_generated_set_angle (GIMP_BRUSH_GENERATED (brush),
+                                      angle_in);
+      angle_out = GIMP_BRUSH_GENERATED (brush)->angle;
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+@headers = qw(<string.h>
+              "core/gimp.h"
+              "core/gimpbrush.h"
+              "core/gimpbrushgenerated.h"
+              "core/gimpcontext.h" 
+              "core/gimpdatafactory.h"
+              "core/gimptempbuf.h"
+              "gimppdb-utils.h");
+
+@procs = qw(brush_new
+            brush_duplicate
+            brush_is_generated 
+            brush_rename
+            brush_delete
+            brush_is_editable
+            brush_get_info
+            brush_get_pixels
+            brush_get_spacing brush_set_spacing
+            brush_get_shape brush_set_shape
+            brush_get_radius brush_set_radius
+            brush_get_spikes brush_set_spikes
+            brush_get_hardness brush_set_hardness
+            brush_get_aspect_ratio brush_set_aspect_ratio
+            brush_get_angle brush_set_angle);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Brush';
+$doc_title = 'gimpbrush';
+$doc_short_desc = 'Functions operating on a single brush.';
+$doc_long_desc = 'Functions operating on a single brush.';
+
+1;
diff --git a/pdb/groups/brush_select.pdb b/pdb/groups/brush_select.pdb
new file mode 100644
index 0000000..a3acedb
--- /dev/null
+++ b/pdb/groups/brush_select.pdb
@@ -0,0 +1,144 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub brushes_popup {
+    $blurb = 'Invokes the Gimp brush selection.';
+    $help  = 'This procedure opens the brush selection dialog.';
+
+    &andy_pdb_misc('1998');
+
+    @inargs = (
+       { name => 'brush_callback', type => 'string', non_empty => 1,
+         desc => 'The callback PDB proc to call when brush selection is
+                  made' },
+       { name => 'popup_title', type => 'string',
+         desc => 'Title of the brush selection dialog' },
+       { name => 'initial_brush', type => 'string', null_ok => 1,
+         desc => 'The name of the brush to set as the first selected' },
+       { name => 'opacity', type => '0 <= float <= 100',
+         desc => 'The initial opacity of the brush' },
+       { name => 'spacing', type => 'int32 <= 1000',
+         desc => 'The initial spacing of the brush (if < 0 then use brush
+                   default spacing)' },
+       { name => 'paint_mode', type => 'enum GimpLayerMode',
+          default => 'GIMP_LAYER_MODE_NORMAL',
+         desc => 'The initial paint mode' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (paint_mode == GIMP_LAYER_MODE_OVERLAY_LEGACY)
+    paint_mode = GIMP_LAYER_MODE_SOFTLIGHT_LEGACY;
+
+  if (gimp->no_interface ||
+      ! gimp_pdb_lookup_procedure (gimp->pdb, brush_callback) ||
+      ! gimp_pdb_dialog_new (gimp, context, progress,
+                             gimp_data_factory_get_container (gimp->brush_factory),
+                             popup_title, brush_callback, initial_brush,
+                             "opacity",    opacity / 100.0,
+                             "paint-mode", paint_mode,
+                             "spacing",    spacing,
+                             NULL))
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub brushes_close_popup {
+    $blurb = 'Close the brush selection dialog.';
+    $help  = 'This procedure closes an opened brush selection dialog.';
+
+    &andy_pdb_misc('1998');
+
+    @inargs = (
+       { name => 'brush_callback', type => 'string', non_empty => 1,
+         desc => 'The name of the callback registered for this pop-up' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp->no_interface ||
+      ! gimp_pdb_lookup_procedure (gimp->pdb, brush_callback) ||
+      ! gimp_pdb_dialog_close (gimp, gimp_data_factory_get_container (gimp->brush_factory),
+                               brush_callback))
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub brushes_set_popup {
+    $blurb = 'Sets the current brush in a brush selection dialog.';
+    $help  = $blurb;
+
+    &andy_pdb_misc('1998');
+
+    @inargs = (
+       { name => 'brush_callback', type => 'string', non_empty => 1,
+         desc => 'The name of the callback registered for this pop-up' },
+       { name => 'brush_name', type => 'string',
+         desc => 'The name of the brush to set as selected' },
+       { name => 'opacity', type => '0 <= float <= 100',
+         desc => 'The initial opacity of the brush' },
+       { name => 'spacing', type => 'int32 <= 1000',
+         desc => 'The initial spacing of the brush (if < 0 then use brush
+                   default spacing)' },
+       { name => 'paint_mode', type => 'enum GimpLayerMode',
+          default => 'GIMP_LAYER_MODE_NORMAL',
+         desc => 'The initial paint mode' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (paint_mode == GIMP_LAYER_MODE_OVERLAY_LEGACY)
+    paint_mode = GIMP_LAYER_MODE_SOFTLIGHT_LEGACY;
+
+  if (gimp->no_interface ||
+      ! gimp_pdb_lookup_procedure (gimp->pdb, brush_callback) ||
+      ! gimp_pdb_dialog_set (gimp, gimp_data_factory_get_container (gimp->brush_factory),
+                             brush_callback, brush_name,
+                             "opacity",    opacity / 100.0,
+                             "paint-mode", paint_mode,
+                             "spacing",    spacing,
+                             NULL))
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+@headers = qw("core/gimp.h"
+              "core/gimpdatafactory.h");
+
+@procs = qw(brushes_popup
+            brushes_close_popup
+            brushes_set_popup);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Brush UI';
+$doc_title = 'gimpbrushselect';
+$doc_short_desc = 'Functions providing a brush selection dialog.';
+$doc_long_desc = 'Functions providing a brush selection dialog.';
+
+1;
diff --git a/pdb/groups/brushes.pdb b/pdb/groups/brushes.pdb
new file mode 100644
index 0000000..4c5231c
--- /dev/null
+++ b/pdb/groups/brushes.pdb
@@ -0,0 +1,227 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub brushes_refresh {
+    $blurb = 'Refresh current brushes. This function always succeeds.';
+
+    $help = <<'HELP';
+This procedure retrieves all brushes currently in the user's brush path
+and updates the brush dialogs accordingly.
+HELP
+
+    &seth_pdb_misc('1997');
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_data_factory_data_refresh (gimp->brush_factory, context);
+}
+CODE
+    );
+}
+
+sub brushes_get_list {
+    $blurb = 'Retrieve a complete listing of the available brushes.';
+
+    $help = <<'HELP';
+This procedure returns a complete listing of available GIMP
+brushes. Each name returned can be used as input to the
+gimp_context_set_brush() procedure.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'filter', type => 'string', null_ok => 1,
+          desc => 'An optional regular expression used to filter the list' }
+    );
+
+    @outargs = (
+       { name => 'brush_list', type => 'stringarray',
+         desc => 'The list of brush names',
+         array => { name => 'num_brushes',
+                    desc => 'The number of brushes in the brush list' } }
+    );
+
+    %invoke = (
+        headers => [ qw("core/gimpcontainer-filter.h") ],
+       code    => <<'CODE'
+{
+  brush_list = gimp_container_get_filtered_name_array (gimp_data_factory_get_container (gimp->brush_factory),
+                                                       filter, &num_brushes);
+}
+CODE
+    );
+}
+
+sub brushes_get_brush {
+    &std_pdb_deprecated ('gimp-context-get-brush');
+
+    @outargs = (
+       { name => 'name', type => 'string',
+         desc => 'The brush name' },
+       { name => 'width', type => 'int32',
+         desc => 'The brush width' },
+       { name => 'height', type => 'int32',
+         desc => 'The brush height' },
+       { name => 'spacing', type => '0 <= int32 <= 1000',
+         desc => 'The brush spacing' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpBrush *brush = gimp_context_get_brush (context);
+
+  if (brush)
+    {
+      name    = g_strdup (gimp_object_get_name (brush));
+      width   = gimp_brush_get_width  (brush);
+      height  = gimp_brush_get_height (brush);
+      spacing = gimp_brush_get_spacing (brush);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub brushes_get_spacing {
+    &std_pdb_deprecated ('gimp-brush-get-spacing');
+
+    @outargs = (
+       { name => 'spacing', type => '0 <= int32 <= 1000',
+         desc => 'The brush spacing' }
+    );
+
+    %invoke = (
+       code =><<'CODE'
+{
+  GimpBrush *brush = gimp_context_get_brush (context);
+
+  if (brush)
+    spacing = gimp_brush_get_spacing (brush);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub brushes_set_spacing {
+    &std_pdb_deprecated ('gimp-brush-set-spacing');
+
+    @inargs = (
+       { name => 'spacing', type => '0 <= int32 <= 1000',
+         desc => 'The brush spacing' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_brush_set_spacing (gimp_context_get_brush (context), spacing);
+}
+CODE
+    );
+}
+
+sub brushes_get_brush_data {
+    &std_pdb_deprecated ('gimp-brush-get-pixels');
+
+    @inargs = (
+       { name => 'name', type => 'string', null_ok => 1,
+         desc => 'The brush name ("" means current active brush)' }
+    );
+
+    @outargs = (
+       { name => 'actual_name', type => 'string',
+         desc => 'The brush name' },
+       { name => 'opacity', type => '0 <= float <= 100',
+         desc => 'The brush opacity' },
+       { name => 'spacing', type => '0 <= int32 <= 1000',
+         desc => 'The brush spacing' },
+       { name => 'paint_mode', type => 'enum GimpLayerMode',
+          default => 'GIMP_LAYER_MODE_NORMAL',
+         desc => 'The paint mode' },
+       { name => 'width', type => 'int32',
+         desc => 'The brush width' },
+       { name => 'height', type => 'int32',
+         desc => 'The brush height' },
+       { name => 'mask_data', type => 'int8array',
+         desc => 'The brush mask data',
+         array => { name => 'length',
+                    desc => 'Length of brush mask data' } }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpBrush *brush;
+
+  if (paint_mode == GIMP_LAYER_MODE_OVERLAY_LEGACY)
+    paint_mode = GIMP_LAYER_MODE_SOFTLIGHT_LEGACY;
+
+  if (name && strlen (name))
+    brush = gimp_pdb_get_brush (gimp, name, FALSE, error);
+  else
+    brush = gimp_context_get_brush (context);
+
+  if (brush)
+    {
+      GimpTempBuf *mask = gimp_brush_get_mask (brush);
+
+      actual_name = g_strdup (gimp_object_get_name (brush));
+      opacity     = 1.0;
+      spacing     = gimp_brush_get_spacing (brush);
+      paint_mode  = 0;
+      width       = gimp_brush_get_width  (brush);
+      height      = gimp_brush_get_height (brush);
+      length      = gimp_temp_buf_get_data_size (mask);
+      mask_data   = g_memdup (gimp_temp_buf_get_data (mask), length);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+@headers = qw(<string.h>
+              "core/gimp.h"
+              "core/gimpbrush.h"
+              "core/gimpcontext.h"
+              "core/gimpdatafactory.h"
+              "core/gimptempbuf.h"
+              "gimppdb-utils.h");
+
+@procs = qw(brushes_refresh
+            brushes_get_list
+            brushes_get_brush
+            brushes_get_spacing brushes_set_spacing
+            brushes_get_brush_data);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Brushes';
+$doc_title = 'gimpbrushes';
+$doc_short_desc = 'Functions for manipulating brushes.';
+$doc_long_desc = 'Functions related to getting and setting brushes.';
+
+1;
diff --git a/pdb/groups/buffer.pdb b/pdb/groups/buffer.pdb
new file mode 100644
index 0000000..b4091b0
--- /dev/null
+++ b/pdb/groups/buffer.pdb
@@ -0,0 +1,260 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub buffers_get_list {
+    $blurb = 'Retrieve a complete listing of the available buffers.';
+
+    $help = <<'HELP';
+This procedure returns a complete listing of available named buffers.
+HELP
+
+    &mitch_pdb_misc('2005', '2.4');
+
+    @inargs = (
+       { name => 'filter', type => 'string', null_ok => 1,
+          desc => 'An optional regular expression used to filter the list' }
+    );
+
+    @outargs = (
+       { name => 'buffer_list', type => 'stringarray',
+         desc => 'The list of buffer names',
+         array => { name => 'num_buffers',
+                    desc => 'The number of buffers' } }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  buffer_list = gimp_container_get_filtered_name_array (gimp->named_buffers,
+                                                        filter, &num_buffers);
+}
+CODE
+    );
+}
+
+sub buffer_rename {
+    $blurb = 'Renames a named buffer.';
+    $help  = 'This procedure renames a named buffer.';
+
+    &mitch_pdb_misc('2005', '2.4');
+
+    @inargs = (
+        { name => 'buffer_name', type => 'string', non_empty => 1,
+          desc => 'The buffer name' },
+        { name => 'new_name', type => 'string', non_empty => 1,
+          desc => 'The buffer\'s new name' }
+    );
+
+    @outargs = (
+       { name => 'real_name', type => 'string',
+         desc => 'The real name given to the buffer' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpBuffer *buffer = gimp_pdb_get_buffer (gimp, buffer_name, error);
+
+  if (buffer)
+    {
+      gimp_object_set_name (GIMP_OBJECT (buffer), new_name);
+      real_name = g_strdup (gimp_object_get_name (buffer));
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub buffer_delete {
+    $blurb = 'Deletes a named buffer.';
+    $help  = 'This procedure deletes a named buffer.';
+
+    $author = $copyright = 'David Gowers <neota softhome net>';
+    $date   = '2005';
+    $since  = '2.4';
+
+    @inargs = (
+        { name => 'buffer_name', type => 'string', non_empty => 1,
+          desc => 'The buffer name' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpBuffer *buffer = gimp_pdb_get_buffer (gimp, buffer_name, error);
+
+  if (buffer)
+    success = gimp_container_remove (gimp->named_buffers, GIMP_OBJECT (buffer));
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub buffer_get_width {
+    $blurb = "Retrieves the specified buffer's width.";
+    $help  = "This procedure retrieves the specified named buffer's width.";
+
+    &mitch_pdb_misc('2005', '2.4');
+
+    @inargs = (
+        { name => 'buffer_name', type => 'string', non_empty => 1,
+          desc => 'The buffer name' }
+    );
+
+    @outargs = (
+       { name => 'width', type => 'int32',
+          desc => "The buffer width" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpBuffer *buffer = gimp_pdb_get_buffer (gimp, buffer_name, error);
+
+  if (buffer)
+    width = gimp_buffer_get_width (buffer);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub buffer_get_height {
+    $blurb = "Retrieves the specified buffer's height.";
+    $help  = "This procedure retrieves the specified named buffer's height.";
+
+    &mitch_pdb_misc('2005', '2.4');
+
+    @inargs = (
+        { name => 'buffer_name', type => 'string', non_empty => 1,
+          desc => 'The buffer name' }
+    );
+
+    @outargs = (
+       { name => 'height', type => 'int32',
+          desc => "The buffer height" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpBuffer *buffer = gimp_pdb_get_buffer (gimp, buffer_name, error);
+
+  if (buffer)
+    height = gimp_buffer_get_height (buffer);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub buffer_get_bytes {
+    $blurb = "Retrieves the specified buffer's bytes.";
+    $help  = "This procedure retrieves the specified named buffer's bytes.";
+
+    &mitch_pdb_misc('2005', '2.4');
+
+    @inargs = (
+        { name => 'buffer_name', type => 'string', non_empty => 1,
+          desc => 'The buffer name' }
+    );
+
+    @outargs = (
+       { name => 'bytes', type => 'int32',
+          desc => "The buffer bpp" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpBuffer *buffer = gimp_pdb_get_buffer (gimp, buffer_name, error);
+
+  if (buffer)
+    {
+      const Babl *format = gimp_buffer_get_format (buffer);
+
+      bytes = babl_format_get_bytes_per_pixel (format);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub buffer_get_image_type {
+    $blurb = "Retrieves the specified buffer's image type.";
+    $help  = "This procedure retrieves the specified named buffer's image type.";
+
+    &mitch_pdb_misc('2005', '2.4');
+
+    @inargs = (
+        { name => 'buffer_name', type => 'string', non_empty => 1,
+          desc => 'The buffer name' }
+    );
+
+    @outargs = (
+       { name => 'image_type', type => 'enum GimpImageBaseType',
+          desc => "The buffer image type" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpBuffer *buffer = gimp_pdb_get_buffer (gimp, buffer_name, error);
+
+  if (buffer)
+    image_type = gimp_babl_format_get_image_type (gimp_buffer_get_format (buffer));
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+@headers = qw(<string.h>
+              "gegl/gimp-babl-compat.h"
+              "core/gimp.h"
+              "core/gimpbuffer.h"
+              "core/gimpcontainer.h"
+              "core/gimpcontainer-filter.h"
+             "gimppdb-utils.h");
+
+@procs = qw(buffers_get_list
+            buffer_rename
+            buffer_delete
+           buffer_get_width
+            buffer_get_height
+            buffer_get_bytes
+            buffer_get_image_type);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Buffer procedures';
+$doc_title = 'gimpbuffer';
+$doc_short_desc = 'Functions for manipulating cut buffers.';
+$doc_long_desc = 'Functions related to named cut buffers.';
+
+1;
diff --git a/pdb/groups/channel.pdb b/pdb/groups/channel.pdb
new file mode 100644
index 0000000..0d0ae94
--- /dev/null
+++ b/pdb/groups/channel.pdb
@@ -0,0 +1,382 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub channel_new {
+    $blurb = 'Create a new channel.';
+
+    $help = <<'HELP';
+This procedure creates a new channel with the specified width, height,
+name, opacity and color.
+
+The new channel still needs to be added to the image, as this is not
+automatic. Add the new channel with gimp_image_insert_channel(). Other
+attributes, such as channel visibility, should be set with explicit
+procedure calls.
+
+The channel's contents are undefined initially.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image to which to add the channel' },
+       { name => 'width', type => '1 <= int32 <= GIMP_MAX_IMAGE_SIZE',
+         desc => 'The channel width' },
+       { name => 'height', type => '1 <= int32 <= GIMP_MAX_IMAGE_SIZE',
+         desc => 'The channel height' },
+       { name => 'name', type => 'string',
+         desc => 'The channel name' },
+       { name => 'opacity', type => '0 <= float <= 100',
+         desc => 'The channel opacity' },
+       { name => 'color', type => 'color',
+         desc => 'The channel compositing color'
+       }
+    );
+
+    @outargs = (
+       { name => 'channel', type => 'channel', wrap => 1,
+         desc => 'The newly created channel' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpRGB rgb_color = color;
+
+  rgb_color.a = opacity / 100.0;
+  channel = gimp_channel_new (image, width, height, name, &rgb_color);
+
+  if (! channel)
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub channel_copy {
+    $blurb = 'Copy a channel.';
+
+    $help = <<'HELP';
+This procedure copies the specified channel and returns the copy.
+
+The new channel still needs to be added to the image, as this is not
+automatic. Add the new channel with gimp_image_insert_channel().
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'channel', type => 'channel',
+         desc => 'The channel to copy' }
+    );
+
+    @outargs = (
+       { name => 'channel_copy', type => 'channel',
+         desc => 'The newly copied channel' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpImage *image  = gimp_item_get_image (GIMP_ITEM (channel));
+  gint       width  = gimp_image_get_width  (image);
+  gint       height = gimp_image_get_height (image);
+
+  if (gimp_item_get_width  (GIMP_ITEM (channel)) == width &&
+      gimp_item_get_height (GIMP_ITEM (channel)) == height)
+    {
+      channel_copy = GIMP_CHANNEL (gimp_item_duplicate (GIMP_ITEM (channel),
+                                                        GIMP_TYPE_CHANNEL));
+
+      if (! channel_copy)
+        success = FALSE;
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub channel_combine_masks {
+    $blurb = 'Combine two channel masks.';
+
+    $help = <<'HELP';
+This procedure combines two channel masks.  The result is stored
+in the first channel.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'channel1', type => 'channel',
+         desc => 'The channel1' },
+       { name => 'channel2', type => 'channel',
+         desc => 'The channel2' },
+       { name => 'operation', type => 'enum GimpChannelOps',
+         desc => 'The selection operation' },
+       { name => 'offx', type => 'int32',
+         desc => 'x offset between upper left corner of
+                   channels: (second - first)' },
+       { name => 'offy', type => 'int32',
+         desc => 'y offset between upper left corner of
+                   channels: (second - first)' }
+    );
+
+    %invoke = (
+        headers => [ qw("core/gimpchannel-combine.h" "gimp-intl.h") ],
+        code => <<'CODE'
+{
+  gimp_channel_push_undo (channel1, _("Combine Masks"));
+  gimp_channel_combine_mask (channel1, channel2, operation, offx, offy);
+}
+CODE
+    );
+}
+
+sub channel_new_from_component {
+    $blurb = 'Create a new channel from a color component';
+
+    $help = <<'HELP';
+This procedure creates a new channel from a color component.
+
+The new channel still needs to be added to the image, as this is not
+automatic. Add the new channel with gimp_image_insert_channel(). Other
+attributes, such as channel visibility, should be set with explicit
+procedure calls.
+HELP
+
+    &shlomi_pdb_misc('2005', '2.4');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image to which to add the channel' },
+       { name => 'component', type => 'enum GimpChannelType',
+         desc => 'The image component' },
+       { name => 'name', type => 'string',
+         desc => 'The channel name' },
+    );
+
+    @outargs = (
+       { name => 'channel', type => 'channel',
+         desc => 'The newly created channel' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_image_get_component_format (image, component) != NULL)
+    channel = gimp_channel_new_from_component (image,
+                                               component, name, NULL);
+
+  if (channel)
+    gimp_item_set_visible (GIMP_ITEM (channel), FALSE, FALSE);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub channel_get_show_masked {
+    $blurb = "Get the composite method of the specified channel.";
+
+    $help = <<'HELP';
+This procedure returns the specified channel's composite method. If
+it is TRUE, then the channel is composited with the image so that
+masked regions are shown. Otherwise, selected regions are shown.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'channel', type => 'channel',
+         desc => 'The channel' }
+    );
+
+    @outargs = (
+       { name => 'show_masked', type => 'boolean',
+         desc => 'The channel composite method' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  show_masked = gimp_channel_get_show_masked (channel);
+}
+CODE
+    );
+}
+
+sub channel_set_show_masked {
+    $blurb = "Set the composite method of the specified channel.";
+
+    $help = <<'HELP';
+This procedure sets the specified channel's composite method. If
+it is TRUE, then the channel is composited with the image so that
+masked regions are shown. Otherwise, selected regions are shown.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'channel', type => 'channel',
+         desc => 'The channel' },
+       { name => 'show_masked', type => 'boolean',
+         desc => 'The new channel composite method' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_channel_set_show_masked (channel, show_masked);
+}
+CODE
+    );
+}
+
+sub channel_get_opacity {
+    $blurb = "Get the opacity of the specified channel.";
+
+    $help = <<'HELP';
+This procedure returns the specified channel's opacity.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'channel', type => 'channel',
+         desc => 'The channel' }
+    );
+
+    @outargs = (
+       { name => 'opacity', type => '0 <= float <= 100',
+         desc => 'The channel opacity' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  opacity = gimp_channel_get_opacity (channel) * 100;
+}
+CODE
+    );
+}
+
+sub channel_set_opacity {
+    $blurb = "Set the opacity of the specified channel.";
+
+    $help = <<'HELP';
+This procedure sets the specified channel's opacity.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'channel', type => 'channel',
+         desc => 'The channel' },
+       { name => 'opacity', type => '0 <= float <= 100',
+         desc => 'The new channel opacity' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_channel_set_opacity (channel, opacity / 100.0, TRUE);
+}
+CODE
+    );
+}
+
+sub channel_get_color {
+    $blurb = "Get the compositing color of the specified channel.";
+
+    $help = <<'HELP';
+This procedure returns the specified channel's compositing color.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'channel', type => 'channel',
+         desc => 'The channel' }
+    );
+
+    @outargs = (
+       { name => 'color', type => 'color', void_ret => 1,
+         desc => 'The channel compositing color' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_channel_get_color (channel, &color);
+  gimp_rgb_set_alpha (&color, 1.0);
+}
+CODE
+    );
+}
+
+sub channel_set_color {
+    $blurb = "Set the compositing color of the specified channel.";
+
+    $help = <<'HELP';
+This procedure sets the specified channel's compositing color.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'channel', type => 'channel',
+         desc => 'The channel' },
+       { name => 'color', type => 'color',
+         desc => 'The new channel compositing color' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpRGB rgb_color = color;
+
+  rgb_color.a = channel->color.a;
+  gimp_channel_set_color (channel, &rgb_color, TRUE);
+}
+CODE
+    );
+}
+
+
+@headers = qw("libgimpbase/gimpbase.h");
+
+@procs = qw(channel_new
+            channel_new_from_component
+            channel_copy
+            channel_combine_masks
+            channel_get_show_masked channel_set_show_masked
+            channel_get_opacity channel_set_opacity
+            channel_get_color channel_set_color);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Channel';
+$doc_title = 'gimpchannel';
+$doc_short_desc = 'Functions for manipulating channels.';
+$doc_long_desc = 'Functions for manipulating channels.';
+
+1;
diff --git a/pdb/groups/color.pdb b/pdb/groups/color.pdb
new file mode 100644
index 0000000..0091e36
--- /dev/null
+++ b/pdb/groups/color.pdb
@@ -0,0 +1,759 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub brightness_contrast {
+    &std_pdb_deprecated ('gimp-drawable-brightness-contrast');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' },
+       { name => 'brightness', type => '-127 <= int32 <= 127',
+         desc => 'Brightness adjustment' },
+       { name => 'contrast', type => '-127 <= int32 <= 127',
+         desc => 'Contrast adjustment' }
+    );
+
+    %invoke = (
+        headers => [ qw("operations/gimpbrightnesscontrastconfig.h") ],
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GObject *config = g_object_new (GIMP_TYPE_BRIGHTNESS_CONTRAST_CONFIG,
+                                      "brightness", brightness / 127.0,
+                                      "contrast",   contrast   / 127.0,
+                                      NULL);
+
+      gimp_drawable_apply_operation_by_name (drawable, progress,
+                                             C_("undo-type", "Brightness-Contrast"),
+                                             "gimp:brightness-contrast",
+                                             config);
+      g_object_unref (config);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub levels {
+    &std_pdb_deprecated ('gimp-drawable-levels');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' },
+       { name => 'channel', type => 'enum GimpHistogramChannel',
+         desc => 'The channel to modify' },
+       { name => 'low_input', type => '0 <= int32 <= 255',
+         desc => "Intensity of lowest input" },
+       { name => 'high_input', type => '0 <= int32 <= 255',
+         desc => "Intensity of highest input" },
+       { name => 'gamma', type => '0.1 <= float <= 10',
+         desc => 'Gamma adjustment factor' },
+       { name => 'low_output', type => '0 <= int32 <= 255',
+         desc => "Intensity of lowest output" },
+       { name => 'high_output', type => '0 <= int32 <= 255',
+         desc => "Intensity of highest output" }
+    );
+
+    %invoke = (
+       headers => [ qw("operations/gimplevelsconfig.h") ],
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error) &&
+      channel != GIMP_HISTOGRAM_LUMINANCE &&
+      (gimp_drawable_has_alpha (drawable) || channel != GIMP_HISTOGRAM_ALPHA) &&
+      (! gimp_drawable_is_gray (drawable) ||
+       channel == GIMP_HISTOGRAM_VALUE || channel == GIMP_HISTOGRAM_ALPHA))
+    {
+      GObject *config = g_object_new (GIMP_TYPE_LEVELS_CONFIG,
+                                      "channel", channel,
+                                      NULL);
+
+      g_object_set (config,
+                    "low-input",   low_input   / 255.0,
+                    "high-input",  high_input  / 255.0,
+                    "gamma",       gamma,
+                    "low-output",  low_output  / 255.0,
+                    "high-output", high_output / 255.0,
+                    NULL);
+
+      gimp_drawable_apply_operation_by_name (drawable, progress,
+                                             C_("undo-type", "Levels"),
+                                             "gimp:levels",
+                                             config);
+      g_object_unref (config);
+    }
+  else
+    success = TRUE;
+}
+CODE
+    );
+}
+
+sub levels_stretch {
+    &std_pdb_deprecated ('gimp-drawable-levels-stretch');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' }
+    );
+
+    %invoke = (
+        headers => [ qw("core/gimpdrawable-levels.h") ],
+        code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      gimp_drawable_levels_stretch (drawable, progress);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub levels_auto {
+    &std_pdb_deprecated ('gimp-drawable-levels-stretch');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' }
+    );
+
+    %invoke = (
+        headers => [ qw("core/gimpdrawable-levels.h") ],
+        code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      gimp_drawable_levels_stretch (drawable, progress);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub posterize {
+    &std_pdb_deprecated ('gimp-drawable-posterize');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' },
+       { name => 'levels', type => '2 <= int32 <= 255',
+         desc => 'Levels of posterization' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node =
+        gegl_node_new_child (NULL,
+                             "operation", "gimp:posterize",
+                             "levels",    levels,
+                             NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Posterize"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub desaturate {
+    &std_pdb_deprecated ('gimp-drawable-desaturate');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error) &&
+      gimp_drawable_is_rgb (drawable))
+    {
+      GeglNode *node =
+        gegl_node_new_child (NULL,
+                             "operation", "gimp:desaturate",
+                             "mode",      GIMP_DESATURATE_LIGHTNESS,
+                             NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Desaturate"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub desaturate_full {
+    &std_pdb_deprecated ('gimp-drawable-desaturate');
+    $since = '2.4';
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' },
+       { name => 'desaturate_mode', type => 'enum GimpDesaturateMode',
+         desc => 'The formula to use to desaturate' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error) &&
+      gimp_drawable_is_rgb (drawable))
+    {
+      GeglNode *node =
+        gegl_node_new_child (NULL,
+                             "operation", "gimp:desaturate",
+                             "mode",      desaturate_mode,
+                             NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Desaturate"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub equalize {
+    &std_pdb_deprecated ('gimp-drawable-equalize');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' },
+       { name => 'mask_only', type => 'boolean',
+         desc => 'Equalization option' }
+    );
+
+    %invoke = (
+       headers => [ qw("core/gimpdrawable-equalize.h") ],
+       code => <<'CODE'
+{
+  if (! gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                   GIMP_PDB_ITEM_CONTENT, error) ||
+      ! gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    success = FALSE;
+
+  if (success)
+    gimp_drawable_equalize (drawable, mask_only);
+}
+CODE
+    );
+}
+
+sub invert {
+    &std_pdb_deprecated ('gimp-drawable-invert');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      gimp_drawable_apply_operation_by_name (drawable, progress,
+                                             _("Invert"),
+                                             "gegl:invert-gamma",
+                                             NULL);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub curves_spline {
+    &std_pdb_deprecated ('gimp-drawable-curves-spline');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' },
+       { name => 'channel', type => 'enum GimpHistogramChannel',
+         desc => 'The channel to modify' },
+       { name => 'control_pts', type => 'int8array',
+         desc => 'The spline control points: { cp1.x, cp1.y, cp2.x, cp2.y,
+                  ... }',
+         array => { name => 'num_points', type => '4 <= int32 <= 34',
+                    desc => 'The number of values in the control point array' }
+       }
+    );
+
+    %invoke = (
+       headers => [ qw("operations/gimpcurvesconfig.h") ],
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error) &&
+      ! (num_points & 1) &&
+      (gimp_drawable_has_alpha (drawable) || channel != GIMP_HISTOGRAM_ALPHA) &&
+      (! gimp_drawable_is_gray (drawable) ||
+       channel == GIMP_HISTOGRAM_VALUE || channel == GIMP_HISTOGRAM_ALPHA)  &&
+       channel != GIMP_HISTOGRAM_LUMINANCE)
+    {
+      GObject *config = gimp_curves_config_new_spline_cruft (channel,
+                                                             control_pts,
+                                                             num_points / 2);
+
+      gimp_drawable_apply_operation_by_name (drawable, progress,
+                                             C_("undo-type", "Curves"),
+                                             "gimp:curves",
+                                             config);
+      g_object_unref (config);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub curves_explicit {
+    &std_pdb_deprecated ('gimp-drawable-curves-explicit');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' },
+       { name => 'channel', type => 'enum GimpHistogramChannel',
+         desc => 'The channel to modify' },
+       { name => 'curve', type => 'int8array',
+         desc => 'The explicit curve',
+         array => { name => 'num_bytes',
+                    desc => 'The number of bytes in the new curve (always
+                             256)' } }
+    );
+
+    %invoke = (
+       headers => [ qw("operations/gimpcurvesconfig.h") ],
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error) &&
+      (num_bytes == 256) &&
+      (gimp_drawable_has_alpha (drawable) || channel != GIMP_HISTOGRAM_ALPHA) &&
+      (! gimp_drawable_is_gray (drawable) ||
+       channel == GIMP_HISTOGRAM_VALUE || channel == GIMP_HISTOGRAM_ALPHA) &&
+       channel != GIMP_HISTOGRAM_LUMINANCE)
+    {
+      GObject *config = gimp_curves_config_new_explicit_cruft (channel,
+                                                               curve,
+                                                               num_bytes);
+
+      gimp_drawable_apply_operation_by_name (drawable, progress,
+                                             C_("undo-type", "Curves"),
+                                             "gimp:curves",
+                                             config);
+      g_object_unref (config);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub color_balance {
+    $blurb = 'Modify the color balance of the specified drawable.';
+
+    $help = <<'HELP';
+Modify the color balance of the specified drawable. There are three axis which
+can be modified: cyan-red, magenta-green, and yellow-blue. Negative values
+increase the amount of the former, positive values increase the amount of the
+latter. Color balance can be controlled with the 'transfer_mode' setting, which
+allows shadows, mid-tones, and highlights in an image to be affected
+differently. The 'preserve-lum' parameter, if TRUE, ensures that the
+luminosity of each pixel remains fixed.
+HELP
+
+    &std_pdb_misc;
+    $date = '1997';
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' },
+       { name => 'transfer_mode', type => 'enum GimpTransferMode',
+         desc => 'Transfer mode' },
+       { name => 'preserve_lum', type => 'boolean',
+         desc => 'Preserve luminosity values at each pixel' },
+       { name => 'cyan_red', type => '-100 <= float <= 100',
+         desc => 'Cyan-Red color balance' },
+       { name => 'magenta_green', type => '-100 <= float <= 100',
+         desc => 'Magenta-Green color balance' },
+       { name => 'yellow_blue', type => '-100 <= float <= 100',
+         desc => 'Yellow-Blue color balance' }
+    );
+
+    %invoke = (
+       headers => [ qw("operations/gimpcolorbalanceconfig.h") ],
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error)  &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GObject *config = g_object_new (GIMP_TYPE_COLOR_BALANCE_CONFIG,
+                                      "range",               transfer_mode,
+                                      "preserve-luminosity", preserve_lum,
+                                      NULL);
+
+      g_object_set (config,
+                    "cyan-red",      cyan_red      / 100.0,
+                    "magenta-green", magenta_green / 100.0,
+                    "yellow-blue",   yellow_blue   / 100.0,
+                    NULL);
+
+      gimp_drawable_apply_operation_by_name (drawable, progress,
+                                             C_("undo-type", "Color Balance"),
+                                             "gimp:color-balance",
+                                             config);
+      g_object_unref (config);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub colorize {
+    &std_pdb_deprecated ('gimp-drawable-colorize-hsl');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' },
+       { name => 'hue', type => '0 <= float <= 360',
+         desc => 'Hue in degrees' },
+       { name => 'saturation', type => '0 <= float <= 100',
+         desc => 'Saturation in percent' },
+       { name => 'lightness', type => '-100 <= float <= 100',
+         desc => 'Lightness in percent' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error) &&
+      ! gimp_drawable_is_gray (drawable))
+    {
+      GeglNode *node =
+        gegl_node_new_child (NULL,
+                             "operation", "gimp:colorize",
+                             "hue",        hue        / 360.0,
+                             "saturation", saturation / 100.0,
+                             "lightness",  lightness  / 100.0,
+                             NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Colorize"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub histogram {
+    $blurb = <<'BLURB';
+Returns information on the intensity histogram for the specified drawable.
+BLURB
+
+    $help = <<'HELP';
+This tool makes it possible to gather information about the intensity
+histogram of a drawable. A channel to examine is first specified. This
+can be either value, red, green, or blue, depending on whether the
+drawable is of type color or grayscale. Second, a range of intensities
+are specified. The gimp_histogram() function returns statistics based
+on the pixels in the drawable that fall under this range of
+values. Mean, standard deviation, median, number of pixels, and
+percentile are all returned.  Additionally, the total count of pixels
+in the image is returned. Counts of pixels are weighted by any
+associated alpha values and by the current selection mask. That is,
+pixels that lie outside an active selection mask will not be
+counted. Similarly, pixels with transparent alpha values will not be
+counted. The returned mean, std_dev and median are in the range (0..255)
+for 8-bit images, or if the plug-in is not precision-aware, and in the
+range (0.0..1.0) otherwise.
+HELP
+
+    &std_pdb_deprecated ('gimp-drawable-histogram');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' },
+       { name => 'channel', type => 'enum GimpHistogramChannel',
+         desc => 'The channel to modify' },
+       { name => 'start_range', type => '0 <= int32 < 256',
+         desc => 'Start of the intensity measurement range' },
+       { name => 'end_range', type => '0 <= int32 < 256',
+         desc => 'End of the intensity measurement range' }
+    );
+
+    @outargs = (
+       { name => 'mean', type => 'float', void_ret => 1,
+         desc => 'Mean intensity value' },
+       { name => 'std_dev',  type => 'float',
+         desc => 'Standard deviation of intensity values' },
+       { name => 'median',  type => 'float',
+         desc => 'Median intensity value' },
+       { name => 'pixels',  type => 'float',
+         desc => 'Alpha-weighted pixel count for entire image' },
+       { name => 'count',  type => 'float',
+         desc => 'Alpha-weighted pixel count for range' },
+       { name => 'percentile',  type => 'float',
+         desc => 'Percentile that range falls under' }
+    );
+
+    %invoke = (
+       headers => [ qw("core/gimpdrawable-histogram.h"
+                        "core/gimphistogram.h") ],
+       code => <<'CODE'
+{
+  if (! gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL, 0, error) ||
+      (! gimp_drawable_has_alpha (drawable) &&
+       channel == GIMP_HISTOGRAM_ALPHA) ||
+      (gimp_drawable_is_gray (drawable) &&
+       channel != GIMP_HISTOGRAM_VALUE && channel != GIMP_HISTOGRAM_ALPHA))
+    success = FALSE;
+
+  if (success)
+    {
+      GimpHistogram *histogram;
+      gint           start = start_range;
+      gint           end   = end_range;
+      gboolean       precision_enabled;
+      gboolean       linear;
+      gint           n_bins;
+
+      precision_enabled =
+        gimp->plug_in_manager->current_plug_in &&
+        gimp_plug_in_precision_enabled (gimp->plug_in_manager->current_plug_in);
+
+      if (precision_enabled)
+        linear = gimp_drawable_get_linear (drawable);
+      else
+        linear = FALSE;
+
+      histogram = gimp_histogram_new (linear);
+      gimp_drawable_calculate_histogram (drawable, histogram, FALSE);
+
+      n_bins = gimp_histogram_n_bins (histogram);
+
+      if (n_bins != 256)
+        {
+          start = ROUND ((gdouble) start * (n_bins - 1) / 255);
+          end   = ROUND ((gdouble) end   * (n_bins - 1) / 255);
+        }
+
+      mean       = gimp_histogram_get_mean (histogram, channel,
+                                           start, end);
+      std_dev    = gimp_histogram_get_std_dev (histogram, channel,
+                                              start, end);
+      median     = gimp_histogram_get_median (histogram, channel,
+                                             start, end);
+      pixels     = gimp_histogram_get_count (histogram, channel, 0, n_bins - 1);
+      count      = gimp_histogram_get_count (histogram, channel,
+                                             start, end);
+      percentile = count / pixels;
+
+      g_object_unref (histogram);
+
+      if (n_bins == 256 || ! precision_enabled)
+        {
+          mean    *= 255;
+          std_dev *= 255;
+          median  *= 255;
+        }
+    }
+}
+CODE
+    );
+}
+
+sub hue_saturation {
+    &std_pdb_deprecated ('gimp-drawable-hue-saturation');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' },
+       { name => 'hue_range', type => 'enum GimpHueRange',
+         desc => 'Range of affected hues' },
+       { name => 'hue_offset', type => '-180 <= float <= 180',
+         desc => 'Hue offset in degrees' },
+       { name => 'lightness', type => '-100 <= float <= 100',
+         desc => 'Lightness modification' },
+       { name => 'saturation', type => '-100 <= float <= 100',
+         desc => 'Saturation modification' }
+    );
+
+    %invoke = (
+       headers => [ qw("operations/gimphuesaturationconfig.h") ],
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GObject *config = g_object_new (GIMP_TYPE_HUE_SATURATION_CONFIG,
+                                      "range", hue_range,
+                                      NULL);
+
+       g_object_set (config,
+                     "hue",        hue_offset / 180.0,
+                     "saturation", saturation / 100.0,
+                     "lightness",  lightness  / 100.0,
+                     NULL);
+
+      gimp_drawable_apply_operation_by_name (drawable, progress,
+                                             _("Hue-Saturation"),
+                                             "gimp:hue-saturation",
+                                             config);
+      g_object_unref (config);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub threshold {
+    $blurb = 'Threshold the specified drawable.';
+
+    $help = <<'HELP';
+This procedures generates a threshold map of the specified drawable. All pixels
+between the values of 'low_threshold' and 'high_threshold' are replaced with
+white, and all other pixels with black.
+HELP
+
+    &std_pdb_deprecated ('gimp-drawable-threshold');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' },
+       { name => 'low_threshold', type => '0 <= int32 <= 255',
+         desc => 'The low threshold value' },
+       { name => 'high_threshold', type => '0 <= int32 <= 255',
+         desc => 'The high threshold value' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node =
+        gegl_node_new_child (NULL,
+                             "operation", "gimp:threshold",
+                             "low",       low_threshold  / 255.0,
+                             "high",      high_threshold / 255.0,
+                             NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Threshold"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+@headers = qw("libgimpmath/gimpmath.h"
+              "core/gimp.h"
+              "core/gimpdrawable.h"
+              "core/gimpdrawable-operation.h"
+              "plug-in/gimpplugin.h"
+              "plug-in/gimppluginmanager.h"
+              "gimppdb-utils.h"
+              "gimp-intl.h");
+
+@procs = qw(brightness_contrast
+            levels levels_auto levels_stretch
+            posterize
+            desaturate desaturate_full
+            equalize
+            invert
+            curves_spline curves_explicit
+            color_balance
+            colorize
+            histogram
+            hue_saturation
+            threshold);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Color';
+$doc_title = 'gimpcolor';
+$doc_short_desc = 'Functions for manipulating color.';
+$doc_long_desc = 'Functions for manipulating color, including curves and histograms.';
+
+1;
diff --git a/pdb/groups/context.pdb b/pdb/groups/context.pdb
new file mode 100644
index 0000000..ef4c4e0
--- /dev/null
+++ b/pdb/groups/context.pdb
@@ -0,0 +1,3111 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub pdb_misc {
+   $author = 'Michael Natterer <mitch gimp org> & Sven Neumann <sven gimp org>';
+   $copyright = 'Michael Natterer & Sven Neumann';
+   $date = '2004';
+   $since = '2.2';
+}
+
+sub context_push {
+    $blurb = 'Pushes a context to the top of the plug-in\'s context stack.';
+
+    $help = <<'HELP';
+This procedure creates a new context by copying the current context. This
+copy becomes the new current context for the calling plug-in until it is
+popped again using gimp_context_pop().
+HELP
+
+    &pdb_misc;
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPlugIn *plug_in = gimp->plug_in_manager->current_plug_in;
+
+  if (plug_in && plug_in->open)
+    success = gimp_plug_in_context_push (plug_in);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub context_pop {
+    $blurb = 'Pops the topmost context from the plug-in\'s context stack.';
+
+    $help = <<'HELP';
+This procedure removes the topmost context from the plug-in's context
+stack.  The context that was active before the corresponding call to
+gimp_context_push() becomes the new current context of the plug-in.
+HELP
+
+    &pdb_misc;
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPlugIn *plug_in = gimp->plug_in_manager->current_plug_in;
+
+  if (plug_in && plug_in->open)
+    success = gimp_plug_in_context_pop (plug_in);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub context_set_defaults {
+    $blurb = 'Reset context settings to their default values.';
+
+    $help = <<'HELP';
+This procedure resets context settings used by various procedures to their
+default value. This procedure will usually be called after a context push
+so that a script which calls procedures affected by context settings will
+not be affected by changes in the global context.
+HELP
+
+    $author = 'Kevin Cozens <kcozens svn gnome org>';
+    $copyright = 'Kevin Cozens';
+    $date = '2011';
+    $since = '2.8';
+
+    %invoke = (
+       code => <<'CODE'
+{
+    gimp_config_reset (GIMP_CONFIG (context));
+}
+CODE
+    );
+}
+
+sub context_list_paint_methods {
+    $blurb = 'Lists the available paint methods.';
+
+    $help = <<'HELP';
+This procedure lists the names of the available paint methods. Any
+of the results can be used for gimp_context_set_paint_method().
+HELP
+
+    &simon_pdb_misc('2007', '2.4');
+
+    @outargs = (
+        { name => 'paint_methods', type => 'stringarray', void_ret => 1,
+          desc => 'The names of the available paint methods',
+          array => { desc => 'The number of the available paint methods' } }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  paint_methods = gimp_container_get_name_array (gimp->paint_info_list,
+                                                 &num_paint_methods);
+}
+CODE
+    );
+}
+
+sub context_get_paint_method {
+    $blurb = 'Retrieve the currently active paint method.';
+
+    $help = <<'HELP';
+This procedure returns the name of the currently active paint method.
+HELP
+
+    &mitch_pdb_misc('2005', '2.4');
+
+    @outargs = (
+       { name  => 'name', type  => 'string',
+         desc  => 'The name of the active paint method' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPaintInfo *paint_info = gimp_context_get_paint_info (context);
+
+  if (paint_info)
+    name = g_strdup (gimp_object_get_name (paint_info));
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub context_set_paint_method {
+    $blurb = 'Set the specified paint method as the active paint method.';
+
+    $help = <<'HELP';
+This procedure allows the active paint method to be set by specifying
+its name.  The name is simply a string which corresponds to one of the
+names of the available paint methods. If there is no matching method
+found, this procedure will return an error.  Otherwise, the specified
+method becomes active and will be used in all subsequent paint
+operations.
+HELP
+
+    &mitch_pdb_misc('2005', '2.4');
+
+    @inargs = (
+        { name => 'name', type => 'string', non_empty => 1,
+          desc => 'The name of the paint method' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPaintInfo *paint_info = gimp_pdb_get_paint_info (gimp, name, error);
+
+  if (paint_info)
+    gimp_context_set_paint_info (context, paint_info);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub context_get_stroke_method {
+    $blurb = 'Retrieve the currently active stroke method.';
+
+    $help = <<'HELP';
+This procedure returns the currently active stroke method.
+HELP
+
+    &mitch_pdb_misc('2015', '2.10');
+
+    @outargs = (
+       { name  => 'stroke_method', type  => 'enum GimpStrokeMethod',
+         desc  => 'The active stroke method' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpStrokeOptions *options =
+    gimp_pdb_context_get_stroke_options (GIMP_PDB_CONTEXT (context));
+
+  g_object_get (options,
+                "method", &stroke_method,
+               NULL);
+}
+CODE
+    );
+}
+
+sub context_set_stroke_method {
+    $blurb = 'Set the specified stroke method as the active stroke method.';
+
+    $help = <<'HELP';
+This procedure set the specified stroke method as the active stroke
+method. The new method will be used in all subsequent stroke
+operations.
+HELP
+
+    &mitch_pdb_misc('2015', '2.10');
+
+    @inargs = (
+        { name => 'stroke_method', type => 'enum GimpStrokeMethod',
+          desc => 'The new stroke method' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpStrokeOptions *options =
+    gimp_pdb_context_get_stroke_options (GIMP_PDB_CONTEXT (context));
+
+  g_object_set (options,
+                "method", stroke_method,
+               NULL);
+}
+CODE
+    );
+}
+
+sub context_get_foreground {
+    $blurb = "Get the current GIMP foreground color.";
+
+    $help = <<'HELP';
+This procedure returns the current GIMP foreground color. The foreground
+color is used in a variety of tools such as paint tools, blending, and bucket
+fill.
+HELP
+
+    &pdb_misc;
+
+    @outargs = (
+        { name => 'foreground', type => 'color', void_ret => 1,
+          desc => 'The foreground color' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_context_get_foreground (context, &foreground);
+  gimp_rgb_set_alpha (&foreground, 1.0);
+}
+CODE
+    );
+}
+
+sub context_set_foreground {
+    $blurb = "Set the current GIMP foreground color.";
+
+    $help = <<'HELP';
+This procedure sets the current GIMP foreground color. After this is set,
+operations which use foreground such as paint tools, blending, and bucket fill
+will use the new value.
+HELP
+
+    &pdb_misc;
+
+    @inargs = (
+        { name => 'foreground', type => 'color', void_ret => 1,
+          desc => 'The foreground color' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_rgb_set_alpha (&foreground, 1.0);
+  gimp_context_set_foreground (context, &foreground);
+}
+CODE
+    );
+}
+
+sub context_get_background {
+    $blurb = "Get the current GIMP background color.";
+
+    $help = <<'HELP';
+This procedure returns the current GIMP background color. The background
+color is used in a variety of tools such as blending, erasing (with non-alpha
+images), and image filling.
+HELP
+
+    &pdb_misc;
+
+    @outargs = (
+        { name => 'background', type => 'color', void_ret => 1,
+          desc => 'The background color' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_context_get_background (context, &background);
+  gimp_rgb_set_alpha (&background, 1.0);
+}
+CODE
+    );
+}
+
+sub context_set_background {
+    $blurb = "Set the current GIMP background color.";
+
+    $help = <<'HELP';
+This procedure sets the current GIMP background color. After this is set,
+operations which use background such as blending, filling images, clearing,
+and erasing (in non-alpha images) will use the new value.
+HELP
+
+    &pdb_misc;
+
+    @inargs = (
+        { name => 'background', type => 'color', void_ret => 1,
+          desc => 'The background color' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_rgb_set_alpha (&background, 1.0);
+  gimp_context_set_background (context, &background);
+}
+CODE
+    );
+}
+
+sub context_set_default_colors {
+    $blurb = <<'BLURB';
+Set the current GIMP foreground and background colors to black and white.
+BLURB
+
+    $help = <<'HELP';
+This procedure sets the current GIMP foreground and background colors to their
+initial default values, black and white.
+HELP
+
+    &pdb_misc;
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_context_set_default_colors (context);
+}
+CODE
+    );
+}
+
+sub context_swap_colors {
+    $blurb = 'Swap the current GIMP foreground and background colors.';
+
+    $help = <<'HELP';
+This procedure swaps the current GIMP foreground and background colors, so that
+the new foreground color becomes the old background color and vice versa.
+HELP
+
+    &pdb_misc;
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_context_swap_colors (context);
+}
+CODE
+    );
+}
+
+sub context_get_opacity {
+    $blurb = 'Get the opacity.';
+
+    $help = <<'HELP';
+This procedure returns the opacity setting. The return
+value is a floating point number between 0 and 100.
+HELP
+
+    &pdb_misc;
+
+    @outargs = (
+        { name => 'opacity', type => '0 <= float <= 100',
+          desc => 'The opacity' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  opacity = gimp_context_get_opacity (context) * 100.0;
+}
+CODE
+    );
+}
+
+sub context_set_opacity {
+    $blurb = 'Set the opacity.';
+
+    $help = <<'HELP';
+This procedure modifies the opacity setting. The value
+should be a floating point number between 0 and 100.
+HELP
+
+    &pdb_misc;
+
+    @inargs = (
+        { name => 'opacity', type => '0 <= float <= 100',
+          desc => 'The opacity' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_context_set_opacity (context, opacity / 100.0);
+}
+CODE
+    );
+}
+
+sub context_get_paint_mode {
+    $blurb = 'Get the paint mode.';
+
+    $help = <<'HELP';
+This procedure returns the paint-mode setting. The return value
+is an integer which corresponds to the values listed in the argument
+description.
+HELP
+
+    &pdb_misc;
+
+    @outargs = (
+        { name => 'paint_mode', type => 'enum GimpLayerMode',
+          default => 'GIMP_LAYER_MODE_NORMAL',
+          desc => 'The paint mode' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  paint_mode = gimp_context_get_paint_mode (context);
+}
+CODE
+    );
+}
+
+sub context_set_paint_mode {
+    $blurb = 'Set the paint mode.';
+
+    $help = <<'HELP';
+This procedure modifies the paint_mode setting.
+HELP
+
+    &pdb_misc;
+
+    @inargs = (
+        { name => 'paint_mode', type => 'enum GimpLayerMode',
+          default => 'GIMP_LAYER_MODE_NORMAL',
+          desc => 'The paint mode' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (paint_mode == GIMP_LAYER_MODE_OVERLAY_LEGACY)
+    paint_mode = GIMP_LAYER_MODE_SOFTLIGHT_LEGACY;
+
+  gimp_context_set_paint_mode (context, paint_mode);
+}
+CODE
+    );
+}
+
+sub context_get_line_width {
+    $blurb = 'Get the line width setting.';
+
+    $help = <<'HELP';
+This procedure returns the line width setting.
+HELP
+
+    &mitch_pdb_misc('2015', '2.10');
+
+    @outargs = (
+        { name => 'line_width', type => '0.0 <= float <= 2000.0',
+          desc => 'The line width setting' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpStrokeOptions *options =
+    gimp_pdb_context_get_stroke_options (GIMP_PDB_CONTEXT (context));
+
+  g_object_get (options,
+                "width", &line_width,
+                NULL);
+}
+CODE
+    );
+}
+
+sub context_set_line_width {
+    $blurb = 'Set the line width setting.';
+
+    $help = <<'HELP';
+This procedure modifies the line width setting for stroking lines.
+
+This setting affects the following procedures:
+gimp_edit_stroke(), gimp_edit_stroke_vectors().
+HELP
+
+    &mitch_pdb_misc('2015', '2.10');
+
+    @inargs = (
+        { name => 'line_width', type => '0.0 <= float <= 2000.0',
+          desc => 'The line width setting' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpStrokeOptions *options =
+    gimp_pdb_context_get_stroke_options (GIMP_PDB_CONTEXT (context));
+
+  g_object_set (options,
+                "width", line_width,
+                NULL);
+}
+CODE
+    );
+}
+
+sub context_get_line_width_unit {
+    $blurb = 'Get the line width unit setting.';
+
+    $help = <<'HELP';
+This procedure returns the line width unit setting.
+HELP
+
+    &mitch_pdb_misc('2015', '2.10');
+
+    @outargs = (
+        { name => 'line_width_unit', type => 'unit',
+          desc => 'The line width unit setting' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpStrokeOptions *options =
+    gimp_pdb_context_get_stroke_options (GIMP_PDB_CONTEXT (context));
+
+  g_object_get (options,
+                "unit", &line_width_unit,
+                NULL);
+}
+CODE
+    );
+}
+
+sub context_set_line_width_unit {
+    $blurb = 'Set the line width unit setting.';
+
+    $help = <<'HELP';
+This procedure modifies the line width unit setting for stroking lines.
+
+This setting affects the following procedures:
+gimp_edit_stroke(), gimp_edit_stroke_vectors().
+HELP
+
+    &mitch_pdb_misc('2015', '2.10');
+
+    @inargs = (
+        { name => 'line_width_unit', type => 'unit',
+          desc => 'The line width setting unit' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpStrokeOptions *options =
+    gimp_pdb_context_get_stroke_options (GIMP_PDB_CONTEXT (context));
+
+  g_object_set (options,
+                "unit", line_width_unit,
+                NULL);
+}
+CODE
+    );
+}
+
+sub context_get_line_cap_style {
+    $blurb = 'Get the line cap style setting.';
+
+    $help = <<'HELP';
+This procedure returns the line cap style setting.
+HELP
+
+    &mitch_pdb_misc('2015', '2.10');
+
+    @outargs = (
+        { name => 'cap_style', type => 'enum GimpCapStyle',
+          desc => 'The line cap style setting' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpStrokeOptions *options =
+    gimp_pdb_context_get_stroke_options (GIMP_PDB_CONTEXT (context));
+
+  g_object_get (options,
+                "cap-style", &cap_style,
+                NULL);
+}
+CODE
+    );
+}
+
+sub context_set_line_cap_style {
+    $blurb = 'Set the line cap style setting.';
+
+    $help = <<'HELP';
+This procedure modifies the line cap style setting for stroking lines.
+
+This setting affects the following procedures:
+gimp_edit_stroke(), gimp_edit_stroke_vectors().
+HELP
+
+    &mitch_pdb_misc('2015', '2.10');
+
+    @inargs = (
+        { name => 'cap_style', type => 'enum GimpCapStyle',
+          desc => 'The line cap style setting' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpStrokeOptions *options =
+    gimp_pdb_context_get_stroke_options (GIMP_PDB_CONTEXT (context));
+
+  g_object_set (options,
+                "cap-style", cap_style,
+                NULL);
+}
+CODE
+    );
+}
+
+sub context_get_line_join_style {
+    $blurb = 'Get the line join style setting.';
+
+    $help = <<'HELP';
+This procedure returns the line join style setting.
+HELP
+
+    &mitch_pdb_misc('2015', '2.10');
+
+    @outargs = (
+        { name => 'join_style', type => 'enum GimpJoinStyle',
+          desc => 'The line join style setting' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpStrokeOptions *options =
+    gimp_pdb_context_get_stroke_options (GIMP_PDB_CONTEXT (context));
+
+  g_object_get (options,
+                "join-style", &join_style,
+                NULL);
+}
+CODE
+    );
+}
+
+sub context_set_line_join_style {
+    $blurb = 'Set the line join style setting.';
+
+    $help = <<'HELP';
+This procedure modifies the line join style setting for stroking lines.
+
+This setting affects the following procedures:
+gimp_edit_stroke(), gimp_edit_stroke_vectors().
+HELP
+
+    &mitch_pdb_misc('2015', '2.10');
+
+    @inargs = (
+        { name => 'join_style', type => 'enum GimpJoinStyle',
+          desc => 'The line join style setting' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpStrokeOptions *options =
+    gimp_pdb_context_get_stroke_options (GIMP_PDB_CONTEXT (context));
+
+  g_object_set (options,
+                "join-style", join_style,
+                NULL);
+}
+CODE
+    );
+}
+
+sub context_get_line_miter_limit {
+    $blurb = 'Get the line miter limit setting.';
+
+    $help = <<'HELP';
+This procedure returns the line miter limit setting.
+HELP
+
+    &mitch_pdb_misc('2015', '2.10');
+
+    @outargs = (
+        { name => 'miter_limit', type => '0.0 <= float <= 100.0',
+          desc => 'The line miter limit setting' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpStrokeOptions *options =
+    gimp_pdb_context_get_stroke_options (GIMP_PDB_CONTEXT (context));
+
+  g_object_get (options,
+                "miter-limit", &miter_limit,
+                NULL);
+}
+CODE
+    );
+}
+
+sub context_set_line_miter_limit {
+    $blurb = 'Set the line miter limit setting.';
+
+    $help = <<'HELP';
+This procedure modifies the line miter limit setting for stroking lines.
+
+A mitered join is converted to a bevelled join if the miter would
+extend to a distance of more than (miter-limit * line-width) from the
+actual join point.
+
+This setting affects the following procedures:
+gimp_edit_stroke(), gimp_edit_stroke_vectors().
+HELP
+
+    &mitch_pdb_misc('2015', '2.10');
+
+    @inargs = (
+        { name => 'miter_limit', type => '0.0 <= float <= 100.0',
+          desc => 'The line miter limit setting' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpStrokeOptions *options =
+    gimp_pdb_context_get_stroke_options (GIMP_PDB_CONTEXT (context));
+
+  g_object_set (options,
+                "miter-limit", miter_limit,
+                NULL);
+}
+CODE
+    );
+}
+
+sub context_get_line_dash_offset {
+    $blurb = 'Get the line dash offset setting.';
+
+    $help = <<'HELP';
+This procedure returns the line dash offset setting.
+HELP
+
+    &mitch_pdb_misc('2015', '2.10');
+
+    @outargs = (
+        { name => 'dash_offset', type => '0.0 <= float <= 2000.0',
+          desc => 'The line dash offset setting' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpStrokeOptions *options =
+    gimp_pdb_context_get_stroke_options (GIMP_PDB_CONTEXT (context));
+
+  g_object_get (options,
+                "dash-offset", &dash_offset,
+                NULL);
+}
+CODE
+    );
+}
+
+sub context_set_line_dash_offset {
+    $blurb = 'Set the line dash offset setting.';
+
+    $help = <<'HELP';
+This procedure modifies the line dash offset setting for stroking lines.
+
+This setting affects the following procedures:
+gimp_edit_stroke(), gimp_edit_stroke_vectors().
+HELP
+
+    &mitch_pdb_misc('2015', '2.10');
+
+    @inargs = (
+        { name => 'dash_offset', type => '0.0 <= float <= 100.0',
+          desc => 'The line dash offset setting' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpStrokeOptions *options =
+    gimp_pdb_context_get_stroke_options (GIMP_PDB_CONTEXT (context));
+
+  g_object_set (options,
+                "dash-offset", dash_offset,
+                NULL);
+}
+CODE
+    );
+}
+
+sub context_get_line_dash_pattern {
+    $blurb = 'Get the line dash pattern setting.';
+
+    $help = <<'HELP';
+This procedure returns the line dash pattern setting.
+HELP
+
+    &mitch_pdb_misc('2015', '2.10');
+
+    @outargs = (
+        { name => 'dashes', type => 'floatarray', void_ret => 1,
+          desc => 'The line dash pattern setting',
+          array => { desc => 'The number of dashes in the dash_pattern array' } }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpStrokeOptions *options =
+    gimp_pdb_context_get_stroke_options (GIMP_PDB_CONTEXT (context));
+
+  GArray *pattern = gimp_stroke_options_get_dash_info (options);
+
+  dashes = gimp_dash_pattern_to_double_array (pattern, &num_dashes);
+}
+CODE
+    );
+}
+
+sub context_set_line_dash_pattern {
+    $blurb = 'Set the line dash pattern setting.';
+
+    $help = <<'HELP';
+This procedure modifies the line dash pattern setting for stroking lines.
+
+The unit of the dash pattern segments is the actual line width used
+for the stroke operation, in other words a segment length of 1.0
+results in a square segment shape (or gap shape).
+
+This setting affects the following procedures:
+gimp_edit_stroke(), gimp_edit_stroke_vectors().
+HELP
+
+    &mitch_pdb_misc('2015', '2.10');
+
+    @inargs = (
+        { name => 'dashes', type => 'floatarray',
+          desc => 'The line dash pattern setting',
+          array => { desc => 'The number of dashes in the dash_pattern array' } }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpStrokeOptions *options =
+    gimp_pdb_context_get_stroke_options (GIMP_PDB_CONTEXT (context));
+
+  GArray *pattern = NULL;
+
+  if (num_dashes > 0)
+    {
+      pattern = gimp_dash_pattern_from_double_array (num_dashes, dashes);
+
+      if (! pattern)
+        success = FALSE;
+    }
+
+  if (success)
+    gimp_stroke_options_take_dash_pattern (options, GIMP_DASH_CUSTOM, pattern);
+}
+CODE
+    );
+}
+
+sub context_get_brush {
+    $blurb = 'Retrieve the currently active brush.';
+
+    $help = <<'HELP';
+This procedure returns the name of the currently active brush.
+All paint operations and stroke operations use this brush to control
+the application of paint to the image.
+HELP
+
+    &pdb_misc;
+
+    @outargs = (
+       { name  => 'name', type  => 'string',
+         desc  => 'The name of the active brush' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpBrush *brush = gimp_context_get_brush (context);
+
+  if (brush)
+    name = g_strdup (gimp_object_get_name (brush));
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub context_set_brush {
+    $blurb = 'Set the specified brush as the active brush.';
+
+    $help = <<'HELP';
+This procedure allows the active brush to be set by specifying its name.
+The name is simply a string which corresponds to one of the names of the
+installed brushes. If there is no matching brush found, this procedure will
+return an error.  Otherwise, the specified brush becomes active and will be
+used in all subsequent paint operations.
+HELP
+
+    &pdb_misc;
+
+    @inargs = (
+        { name => 'name', type => 'string', non_empty => 1,
+          desc => 'The name of the brush' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpBrush *brush = gimp_pdb_get_brush (gimp, name, FALSE, error);
+
+  if (brush)
+    gimp_context_set_brush (context, brush);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub context_get_brush_size {
+    $blurb = 'Get brush size in pixels.';
+    $help = 'Get the brush size in pixels for brush based paint tools.';
+
+    &ejs_pdb_misc('2012', '2.8');
+
+    @outargs = (
+        { name => "size", type => "0 < float",
+          desc => "Brush size in pixels" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  /* all options should have the same value, so pick a random one */
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-paintbrush");
+
+  if (options)
+    g_object_get (options,
+                  "brush-size", &size,
+                   NULL);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub context_set_brush_size {
+    $blurb = 'Set brush size in pixels.';
+    $help = 'Set the brush size in pixels for brush based paint tools.';
+
+    &ejs_pdb_misc('2012', '2.8');
+
+    @inargs = (
+        { name => "size", type => "1 <= float <= 10000",
+          desc => "Brush size in pixels" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GList *options;
+  GList *list;
+
+  options = gimp_pdb_context_get_brush_options (GIMP_PDB_CONTEXT (context));
+
+  for (list = options; list; list = g_list_next (list))
+    g_object_set (list->data,
+                  "brush-size", (gdouble) size,
+                   NULL);
+
+  g_list_free (options);
+}
+CODE
+    );
+}
+
+sub context_set_brush_default_size {
+    $blurb = 'Set brush size to its default.';
+    $help = <<'HELP';
+Set the brush size to the default (max of width and height) for
+paintbrush, airbrush, or pencil tools.
+HELP
+
+    &ejs_pdb_misc('2012', '2.8');
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpBrush *brush = gimp_context_get_brush (context);
+
+  if (brush)
+    {
+      GList *options;
+      GList *list;
+
+      options = gimp_pdb_context_get_brush_options (GIMP_PDB_CONTEXT (context));
+
+      for (list = options; list; list = g_list_next (list))
+        gimp_paint_options_set_default_brush_size (list->data, brush);
+
+      g_list_free (options);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub context_set_brush_aspect_ratio {
+    $blurb = 'Set brush aspect ratio.';
+    $help = 'Set the aspect ratio for brush based paint tools.';
+
+    &ejs_pdb_misc('2012', '2.8');
+
+    @inargs = (
+        { name => "aspect", type => "-20 <= float <= 20",
+          desc => "Aspect ratio" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GList *options;
+  GList *list;
+
+  options = gimp_pdb_context_get_brush_options (GIMP_PDB_CONTEXT (context));
+
+  for (list = options; list; list = g_list_next (list))
+    g_object_set (list->data,
+                  "brush-aspect-ratio", (gdouble) aspect,
+                   NULL);
+
+  g_list_free (options);
+}
+CODE
+    );
+}
+
+sub context_get_brush_aspect_ratio {
+    $blurb = 'Get brush aspect ratio.';
+    $help = 'Set the aspect ratio for brush based paint tools.';
+
+    &ejs_pdb_misc('2012', '2.8');
+
+    @outargs = (
+        { name => "aspect", type => "-20 <= float <= 20",
+          desc => "Aspect ratio" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  /* all options should have the same value, so pick a random one */
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-paintbrush");
+
+  if (options)
+    g_object_get (options,
+                  "brush-aspect-ratio", &aspect,
+                  NULL);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub context_set_brush_angle {
+    $blurb = 'Set brush angle in degrees.';
+    $help = 'Set the angle in degrees for brush based paint tools.';
+
+    &ejs_pdb_misc('2012', '2.8');
+
+    @inargs = (
+       { name => "angle", type => "-180 <= float <= 180",
+         desc => "Angle in degrees" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GList *options;
+  GList *list;
+
+  options = gimp_pdb_context_get_brush_options (GIMP_PDB_CONTEXT (context));
+
+  for (list = options; list; list = g_list_next (list))
+    g_object_set (list->data,
+                  "brush-angle", (gdouble) angle,
+                   NULL);
+
+  g_list_free (options);
+}
+CODE
+    );
+}
+
+sub context_get_brush_angle {
+    $blurb = 'Get brush angle in degrees.';
+    $help = 'Set the angle in degrees for brush based paint tools.';
+
+    &ejs_pdb_misc('2012', '2.8');
+
+    @outargs = (
+       { name => "angle", type => "-180 <= float <= 180",
+         desc => "Angle in degrees" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  /* all options should have the same value, so pick a random one */
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-paintbrush");
+
+  if (options)
+    g_object_get (options,
+                  "brush-angle", &angle,
+                  NULL);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub context_get_brush_spacing {
+    $blurb = 'Get brush spacing as percent of size.';
+    $help = 'Get the brush spacing as percent of size for brush based paint tools.';
+
+    &adeath_pdb_misc('2014', '2.10');
+
+    @outargs = (
+        { name => "spacing", type => "0.01 <= float <= 50.0",
+          desc => "Brush spacing as fraction of size" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  /* all options should have the same value, so pick a random one */
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-paintbrush");
+
+  if (options)
+    g_object_get (options,
+                  "brush-spacing", &spacing,
+                   NULL);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub context_set_brush_spacing {
+    $blurb = 'Set brush spacing as percent of size.';
+    $help = 'Set the brush spacing as percent of size for brush based paint tools.';
+
+    &adeath_pdb_misc('2014', '2.10');
+
+    @inargs = (
+        { name => "spacing", type => "0.01 <= float <= 50.0",
+          desc => "Brush spacing as fraction of size" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GList *options;
+  GList *list;
+
+  options = gimp_pdb_context_get_brush_options (GIMP_PDB_CONTEXT (context));
+
+  for (list = options; list; list = g_list_next (list))
+    g_object_set (list->data,
+                  "brush-spacing", (gdouble) spacing,
+                   NULL);
+
+  g_list_free (options);
+}
+CODE
+    );
+}
+
+sub context_set_brush_default_spacing {
+    $blurb = 'Set brush spacing to its default.';
+    $help = <<'HELP';
+Set the brush spacing to the default  for
+paintbrush, airbrush, or pencil tools.
+HELP
+
+    &adeath_pdb_misc('2014', '2.10');
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpBrush *brush = gimp_context_get_brush (context);
+
+  if (brush)
+    {
+      GList *options;
+      GList *list;
+
+      options = gimp_pdb_context_get_brush_options (GIMP_PDB_CONTEXT (context));
+
+      for (list = options; list; list = g_list_next (list))
+        gimp_paint_options_set_default_brush_spacing (list->data, brush);
+
+      g_list_free (options);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub context_get_brush_hardness {
+    $blurb = 'Get brush hardness in paint options.';
+    $help = 'Get the brush hardness for brush based paint tools.';
+
+    &adeath_pdb_misc('2014', '2.10');
+
+    @outargs = (
+        { name => "hardness", type => "0.0 <= float <= 1.0",
+          desc => "Brush hardness" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  /* all options should have the same value, so pick a random one */
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-paintbrush");
+
+  if (options)
+    g_object_get (options,
+                  "brush-hardness", &hardness,
+                   NULL);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub context_set_brush_hardness {
+    $blurb = 'Set brush hardness.';
+    $help = 'Set the brush hardness for brush based paint tools.';
+
+    &adeath_pdb_misc('2014', '2.10');
+
+    @inargs = (
+        { name => "hardness", type => "0.0 <= float <= 1.0",
+          desc => "Brush hardness" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GList *options;
+  GList *list;
+
+  options = gimp_pdb_context_get_brush_options (GIMP_PDB_CONTEXT (context));
+
+  for (list = options; list; list = g_list_next (list))
+    g_object_set (list->data,
+                  "brush-hardness", (gdouble) hardness,
+                   NULL);
+
+  g_list_free (options);
+}
+CODE
+    );
+}
+
+sub context_set_brush_default_hardness {
+    $blurb = 'Set brush spacing to its default.';
+    $help = <<'HELP';
+Set the brush spacing to the default  for
+paintbrush, airbrush, or pencil tools.
+HELP
+
+    &adeath_pdb_misc('2014', '2.10');
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpBrush *brush = gimp_context_get_brush (context);
+
+  if (brush)
+    {
+      GList *options;
+      GList *list;
+
+      options = gimp_pdb_context_get_brush_options (GIMP_PDB_CONTEXT (context));
+
+      for (list = options; list; list = g_list_next (list))
+        gimp_paint_options_set_default_brush_hardness (list->data, brush);
+
+      g_list_free (options);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub context_get_brush_force {
+    $blurb = 'Get brush force in paint options.';
+    $help = 'Get the brush application force for brush based paint tools.';
+
+    &adeath_pdb_misc('2014', '2.10');
+
+    @outargs = (
+        { name => "force", type => "0.0 <= float <= 1.0",
+          desc => "Brush application force" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  /* all options should have the same value, so pick a random one */
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-paintbrush");
+
+  if (options)
+    g_object_get (options,
+                  "brush-force", &force,
+                   NULL);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub context_set_brush_force {
+    $blurb = 'Set brush application force.';
+    $help = 'Set the brush application force for brush based paint tools.';
+
+    &adeath_pdb_misc('2014', '2.10');
+
+    @inargs = (
+        { name => "force", type => "0.0 <= float <= 1.0",
+          desc => "Brush application force" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GList *options;
+  GList *list;
+
+  options = gimp_pdb_context_get_brush_options (GIMP_PDB_CONTEXT (context));
+
+  for (list = options; list; list = g_list_next (list))
+    g_object_set (list->data,
+                  "brush-force", (gdouble) force,
+                   NULL);
+
+  g_list_free (options);
+}
+CODE
+    );
+}
+
+sub context_get_dynamics {
+    $blurb = 'Retrieve the currently active paint dynamics.';
+
+    $help = <<'HELP';
+This procedure returns the name of the currently active paint
+dynamics.  All paint operations and stroke operations use this paint
+dynamics to control the application of paint to the image.
+HELP
+
+    &mitch_pdb_misc('2011', '2.8');
+
+    @outargs = (
+       { name  => 'name', type  => 'string',
+         desc  => 'The name of the active paint dynamics' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpDynamics *dynamics = gimp_context_get_dynamics (context);
+
+  if (dynamics)
+    name = g_strdup (gimp_object_get_name (dynamics));
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub context_set_dynamics {
+    $blurb = 'Set the specified paint dynamics as the active paint dynamics.';
+
+    $help = <<'HELP';
+This procedure allows the active paint dynamics to be set by
+specifying its name.  The name is simply a string which corresponds to
+one of the names of the installed paint dynamics. If there is no
+matching paint dynamics found, this procedure will return an error.
+Otherwise, the specified paint dynamics becomes active and will be
+used in all subsequent paint operations.
+HELP
+
+    &mitch_pdb_misc('2011', '2.8');
+
+    @inargs = (
+        { name => 'name', type => 'string', non_empty => 1,
+          desc => 'The name of the paint dynamics' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpDynamics *dynamics = gimp_pdb_get_dynamics (gimp, name, GIMP_PDB_DATA_ACCESS_READ, error);
+
+  if (dynamics)
+    gimp_context_set_dynamics (context, dynamics);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub context_get_mypaint_brush {
+    $blurb = 'Retrieve the currently active MyPaint brush.';
+
+    $help = <<'HELP';
+This procedure returns the name of the currently active MyPaint brush.
+HELP
+
+    &mitch_pdb_misc('2016', '2.10');
+
+    @outargs = (
+       { name  => 'name', type  => 'string',
+         desc  => 'The name of the active MyPaint brush' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpMybrush *brush = gimp_context_get_mybrush (context);
+
+  if (brush)
+    name = g_strdup (gimp_object_get_name (brush));
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub context_set_mypaint_brush {
+    $blurb = 'Set the specified MyPaint brush as the active MyPaint brush.';
+
+    $help = <<'HELP';
+This procedure allows the active MyPaint brush to be set by
+specifying its name.  The name is simply a string which corresponds to
+one of the names of the installed MyPaint brushes. If there is no
+matching MyPaint brush found, this procedure will return an error.
+Otherwise, the specified MyPaint brush becomes active and will be
+used in all subsequent MyPaint paint operations.
+HELP
+
+    &mitch_pdb_misc('2016', '2.10');
+
+    @inargs = (
+        { name => 'name', type => 'string', non_empty => 1,
+          desc => 'The name of the MyPaint brush' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpMybrush *brush = gimp_pdb_get_mybrush (gimp, name, GIMP_PDB_DATA_ACCESS_READ, error);
+
+  if (brush)
+    gimp_context_set_mybrush (context, brush);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub context_get_pattern {
+    $blurb = 'Retrieve the currently active pattern.';
+
+    $help = <<'HELP';
+This procedure returns name of the the currently active pattern.
+All clone and bucket-fill operations with patterns will use this
+pattern to control the application of paint to the image.
+HELP
+
+    &pdb_misc;
+
+    @outargs = (
+       { name  => 'name', type  => 'string',
+         desc  => 'The name of the active pattern' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPattern *pattern = gimp_context_get_pattern (context);
+
+  if (pattern)
+    name = g_strdup (gimp_object_get_name (pattern));
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub context_set_pattern {
+    $blurb = 'Set the specified pattern as the active pattern.';
+
+    $help = <<'HELP';
+This procedure allows the active pattern to be set by specifying its name.
+The name is simply a string which corresponds to one of the names of the
+installed patterns. If there is no matching pattern found, this procedure will
+return an error. Otherwise, the specified pattern becomes active and will be
+used in all subsequent paint operations.
+HELP
+
+    &pdb_misc;
+
+    @inargs = (
+        { name => 'name', type => 'string', non_empty => 1,
+          desc => 'The name of the pattern' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPattern *pattern = gimp_pdb_get_pattern (gimp, name, error);
+
+  if (pattern)
+    gimp_context_set_pattern (context, pattern);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub context_get_gradient {
+    $blurb = 'Retrieve the currently active gradient.';
+
+    $help = <<'HELP';
+This procedure returns the name of the currently active gradient.
+HELP
+
+    &pdb_misc;
+
+    @outargs = (
+       { name  => 'name', type  => 'string',
+         desc  => 'The name of the active gradient' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpGradient *gradient = gimp_context_get_gradient (context);
+
+  if (gradient)
+    name = g_strdup (gimp_object_get_name (gradient));
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub context_set_gradient {
+    $blurb = 'Sets the specified gradient as the active gradient.';
+
+    $help = <<'HELP';
+This procedure lets you set the specified gradient as the active or "current"
+one. The name is simply a string which corresponds to one of the loaded
+gradients. If no matching gradient is found, this
+procedure will return an error. Otherwise, the specified gradient will become
+active and will be used for subsequent custom gradient operations.
+HELP
+
+    &pdb_misc;
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The name of the gradient' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpGradient *gradient = gimp_pdb_get_gradient (gimp, name, FALSE, error);
+
+  if (gradient)
+    gimp_context_set_gradient (context, gradient);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub context_get_palette {
+    $blurb = 'Retrieve the currently active palette.';
+
+    $help = <<'HELP';
+This procedure returns the name of the the currently active palette.
+HELP
+
+    &pdb_misc;
+
+    @outargs = (
+       { name  => 'name', type  => 'string',
+         desc  => 'The name of the active palette' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpPalette *palette = gimp_context_get_palette (context);
+
+  if (palette)
+    name = g_strdup (gimp_object_get_name (palette));
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub context_set_palette {
+    $blurb = 'Set the specified palette as the active palette.';
+
+    $help = <<'HELP';
+This procedure allows the active palette to be set by specifying its name.
+The name is simply a string which corresponds to one of the names of the
+installed palettes. If no matching palette is found, this procedure will
+return an error. Otherwise, the specified palette becomes active and will be
+used in all subsequent palette operations.
+HELP
+
+    &pdb_misc;
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The name of the palette' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpPalette *palette = gimp_pdb_get_palette (gimp, name, FALSE, error);
+
+  if (palette)
+    gimp_context_set_palette (context, palette);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub context_get_font {
+    $blurb = 'Retrieve the currently active font.';
+
+    $help = <<'HELP';
+This procedure returns the name of the currently active font.
+HELP
+
+    &pdb_misc;
+
+    @outargs = (
+       { name  => 'name', type  => 'string',
+         desc  => 'The name of the active font' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpFont *font = gimp_context_get_font (context);
+
+  if (font)
+    name = g_strdup (gimp_object_get_name (font));
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub context_set_font {
+    $blurb = 'Set the specified font as the active font.';
+
+    $help = <<'HELP';
+This procedure allows the active font to be set by specifying its name.
+The name is simply a string which corresponds to one of the names of the
+installed fonts. If no matching font is found, this procedure will
+return an error. Otherwise, the specified font becomes active and will be
+used in all subsequent font operations.
+HELP
+
+    &pdb_misc;
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The name of the font' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpFont *font = gimp_pdb_get_font (gimp, name, error);
+
+  if (font)
+    gimp_context_set_font (context, font);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub context_get_antialias {
+    $blurb = 'Get the antialias setting.';
+
+    $help = <<'HELP';
+This procedure returns the antialias setting.
+HELP
+
+    &mitch_pdb_misc('2010', '2.8');
+
+    @outargs = (
+        { name => 'antialias', type => 'boolean',
+          desc => 'The antialias setting' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  g_object_get (context,
+                "antialias", &antialias,
+                NULL);
+}
+CODE
+    );
+}
+
+sub context_set_antialias {
+    $blurb = 'Set the antialias setting.';
+
+    $help = <<'HELP';
+This procedure modifies the antialias setting. If antialiasing is
+turned on, the edges of selected region will contain intermediate
+values which give the appearance of a sharper, less pixelized edge.
+This should be set as TRUE most of the time unless a binary-only
+selection is wanted.
+
+This setting affects the following procedures:
+gimp_image_select_color(), gimp_image_select_contiguous_color(),
+gimp_image_select_round_rectangle(), gimp_image_select_ellipse(),
+gimp_image_select_polygon(), gimp_image_select_item().
+HELP
+
+    &mitch_pdb_misc('2010', '2.8');
+
+    @inargs = (
+        { name => 'antialias', type => 'boolean',
+          desc => 'The antialias setting' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  g_object_set (context,
+                "antialias", antialias,
+                NULL);
+}
+CODE
+    );
+}
+
+sub context_get_feather {
+    $blurb = 'Get the feather setting.';
+
+    $help = <<'HELP';
+This procedure returns the feather setting.
+HELP
+
+    &mitch_pdb_misc('2010', '2.8');
+
+    @outargs = (
+        { name => 'feather', type => 'boolean',
+          desc => 'The feather setting' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  g_object_get (context,
+                "feather", &feather,
+                NULL);
+}
+CODE
+    );
+}
+
+sub context_set_feather {
+    $blurb = 'Set the feather setting.';
+
+    $help = <<'HELP';
+This procedure modifies the feather setting. If the feather option is
+enabled, selections will be blurred before combining. The blur is a
+gaussian blur; its radii can be controlled using
+gimp_context_set_feather_radius().
+
+This setting affects the following procedures:
+gimp_image_select_color(), gimp_image_select_contiguous_color(),
+gimp_image_select_rectangle(), gimp_image_select_round_rectangle(),
+gimp_image_select_ellipse(), gimp_image_select_polygon(),
+gimp_image_select_item().
+HELP
+
+    &mitch_pdb_misc('2010', '2.8');
+
+    @inargs = (
+        { name => 'feather', type => 'boolean',
+          desc => 'The feather setting' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  g_object_set (context,
+                "feather", feather,
+                NULL);
+}
+CODE
+    );
+}
+
+sub context_get_feather_radius {
+    $blurb = 'Get the feather radius setting.';
+
+    $help = <<'HELP';
+This procedure returns the feather radius setting.
+HELP
+
+    &mitch_pdb_misc('2010', '2.8');
+
+    @outargs = (
+        { name => 'feather_radius_x', type => '0 <= float <= 1000', void_ret => 1,
+          desc => 'The horizontal feather radius' },
+        { name => 'feather_radius_y', type => '0 <= float <= 1000',
+          desc => 'The vertical feather radius' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  g_object_get (context,
+                "feather-radius-x", &feather_radius_x,
+                "feather-radius-y", &feather_radius_y,
+                NULL);
+}
+CODE
+    );
+}
+
+sub context_set_feather_radius {
+    $blurb = 'Set the feather radius setting.';
+
+    $help = <<'HELP';
+This procedure modifies the feather radius setting.
+
+This setting affects all procedures that are affected
+by gimp_context_set_feather().
+HELP
+
+    &mitch_pdb_misc('2010', '2.8');
+
+    @inargs = (
+        { name => 'feather_radius_x', type => '0 <= float <= 1000',
+          desc => 'The horizontal feather radius' },
+        { name => 'feather_radius_y', type => '0 <= float <= 1000',
+          desc => 'The vertical feather radius' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  g_object_set (context,
+                "feather-radius-x", feather_radius_x,
+                "feather-radius-y", feather_radius_y,
+                NULL);
+}
+CODE
+    );
+}
+
+sub context_get_sample_merged {
+    $blurb = 'Get the sample merged setting.';
+
+    $help = <<'HELP';
+This procedure returns the sample merged setting.
+HELP
+
+    &mitch_pdb_misc('2011', '2.8');
+
+    @outargs = (
+        { name => 'sample_merged', type => 'boolean',
+          desc => 'The sample merged setting' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  g_object_get (context,
+                "sample-merged", &sample_merged,
+                NULL);
+}
+CODE
+    );
+}
+
+sub context_set_sample_merged {
+    $blurb = 'Set the sample merged setting.';
+
+    $help = <<'HELP';
+This procedure modifies the sample merged setting. If an operation
+depends on the colors of the pixels present in a drawable, like when
+doing a seed fill, this setting controls whether the pixel data from
+the specified drawable is used ('sample-merged' is FALSE), or the
+pixel data from the composite image ('sample-merged' is TRUE. This is
+equivalent to sampling for colors after merging all visible layers).
+
+This setting affects the following procedures:
+gimp_image_select_color(), gimp_image_select_contiguous_color().
+HELP
+
+    &mitch_pdb_misc('2011', '2.8');
+
+    @inargs = (
+        { name => 'sample_merged', type => 'boolean',
+          desc => 'The sample merged setting' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  g_object_set (context,
+                "sample-merged", sample_merged,
+                NULL);
+}
+CODE
+    );
+}
+
+sub context_get_sample_criterion {
+    $blurb = 'Get the sample criterion setting.';
+
+    $help = <<'HELP';
+This procedure returns the sample criterion setting.
+HELP
+
+    &mitch_pdb_misc('2011', '2.8');
+
+    @outargs = (
+        { name => 'sample_criterion', type => 'enum GimpSelectCriterion',
+          desc => 'The sample criterion setting' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  g_object_get (context,
+                "sample-criterion", &sample_criterion,
+                NULL);
+}
+CODE
+    );
+}
+
+sub context_set_sample_criterion {
+    $blurb = 'Set the sample criterion setting.';
+
+    $help = <<'HELP';
+This procedure modifies the sample criterion setting. If an operation
+depends on the colors of the pixels present in a drawable, like when
+doing a seed fill, this setting controls how color similarity is
+determined. SELECT_CRITERION_COMPOSITE is the default value.
+
+This setting affects the following procedures:
+gimp_image_select_color(), gimp_image_select_contiguous_color().
+HELP
+
+    &mitch_pdb_misc('2011', '2.8');
+
+    @inargs = (
+        { name => 'sample_criterion', type => 'enum GimpSelectCriterion',
+          desc => 'The sample criterion setting' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  g_object_set (context,
+                "sample-criterion", sample_criterion,
+                NULL);
+}
+CODE
+    );
+}
+
+sub context_get_sample_threshold {
+    $blurb = 'Get the sample threshold setting.';
+
+    $help = <<'HELP';
+This procedure returns the sample threshold setting.
+HELP
+
+    &mitch_pdb_misc('2011', '2.8');
+
+    @outargs = (
+        { name => 'sample_threshold', type => '0.0 <= float <= 1.0',
+          desc => 'The sample threshold setting' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  g_object_get (context,
+                "sample-threshold", &sample_threshold,
+                NULL);
+}
+CODE
+    );
+}
+
+sub context_set_sample_threshold {
+    $blurb = 'Set the sample threshold setting.';
+
+    $help = <<'HELP';
+This procedure modifies the sample threshold setting. If an operation
+depends on the colors of the pixels present in a drawable, like when
+doing a seed fill, this setting controls what is "sufficiently close"
+to be considered a similar color. If the sample threshold has not been
+set explicitly, the default threshold set in gimprc will be used.
+
+This setting affects the following procedures:
+gimp_image_select_color(), gimp_image_select_contiguous_color().
+HELP
+
+    &mitch_pdb_misc('2011', '2.8');
+
+    @inargs = (
+        { name => 'sample_threshold', type => '0.0 <= float <= 1.0',
+          desc => 'The sample threshold setting' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  g_object_set (context,
+                "sample-threshold", sample_threshold,
+                NULL);
+}
+CODE
+    );
+}
+
+sub context_get_sample_threshold_int {
+    $blurb = 'Get the sample threshold setting as an integer value.';
+
+    $help = <<'HELP';
+This procedure returns the sample threshold setting as an integer
+value. See gimp_context_get_sample_threshold().
+HELP
+
+    &mitch_pdb_misc('2011', '2.8');
+
+    @outargs = (
+        { name => 'sample_threshold', type => '0 <= int32 <= 255',
+          desc => 'The sample threshold setting' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gdouble threshold;
+
+  g_object_get (context,
+                "sample-threshold", &threshold,
+                NULL);
+
+  sample_threshold = (gint) (threshold * 255.99);
+}
+CODE
+    );
+}
+
+sub context_set_sample_threshold_int {
+    $blurb = 'Set the sample threshold setting as an integer value.';
+
+    $help = <<'HELP';
+This procedure modifies the sample threshold setting as an integer
+value. See gimp_context_set_sample_threshold().
+HELP
+
+    &mitch_pdb_misc('2011', '2.8');
+
+    @inargs = (
+        { name => 'sample_threshold', type => '0 <= int32 <= 255',
+          desc => 'The sample threshold setting' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  g_object_set (context,
+                "sample-threshold", (gdouble) sample_threshold / 255.0,
+                NULL);
+}
+CODE
+    );
+}
+
+sub context_get_sample_transparent {
+    $blurb = 'Get the sample transparent setting.';
+
+    $help = <<'HELP';
+This procedure returns the sample transparent setting.
+HELP
+
+    &mitch_pdb_misc('2011', '2.8');
+
+    @outargs = (
+        { name => 'sample_transparent', type => 'boolean',
+          desc => 'The sample transparent setting' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  g_object_get (context,
+                "sample-transparent", &sample_transparent,
+                NULL);
+}
+CODE
+    );
+}
+
+sub context_set_sample_transparent {
+    $blurb = 'Set the sample transparent setting.';
+
+    $help = <<'HELP';
+This procedure modifies the sample transparent setting. If an
+operation depends on the colors of the pixels present in a drawable,
+like when doing a seed fill, this setting controls whether
+transparency is considered to be a unique selectable color. When this
+setting is TRUE, transparent areas can be selected or filled.
+
+This setting affects the following procedures:
+gimp_image_select_color(), gimp_image_select_contiguous_color().
+HELP
+
+    &mitch_pdb_misc('2011', '2.8');
+
+    @inargs = (
+        { name => 'sample_transparent', type => 'boolean',
+          desc => 'The sample transparent setting' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  g_object_set (context,
+                "sample-transparent", sample_transparent,
+                NULL);
+}
+CODE
+    );
+}
+
+sub context_get_diagonal_neighbors {
+    $blurb = 'Get the diagonal neighbors setting.';
+
+    $help = <<'HELP';
+This procedure returns the diagonal neighbors setting.
+HELP
+
+    $author = 'Ell';
+    $copyright = 'Ell';
+    $date = '2016';
+    $since = '2.10';
+
+    @outargs = (
+        { name => 'diagonal_neighbors', type => 'boolean',
+          desc => 'The diagonal neighbors setting' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  g_object_get (context,
+                "diagonal-neighbors", &diagonal_neighbors,
+                NULL);
+}
+CODE
+    );
+}
+
+sub context_set_diagonal_neighbors {
+    $blurb = 'Set the diagonal neighbors setting.';
+
+    $help = <<'HELP';
+This procedure modifies the diagonal neighbors setting. If the affected
+region of an operation is based on a seed point, like when doing a seed
+fill, then, when this setting is TRUE, all eight neighbors of each pixel
+are considered when calculating the affected region; in contrast, when
+this setting is FALSE, only the four orthogonal neighbors of each pixel
+are considered.
+
+This setting affects the following procedures:
+gimp_image_select_contiguous_color().
+HELP
+
+    $author = 'Ell';
+    $copyright = 'Ell';
+    $date = '2016';
+    $since = '2.10';
+
+    @inargs = (
+        { name => 'diagonal_neighbors', type => 'boolean',
+          desc => 'The diagonal neighbors setting' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  g_object_set (context,
+                "diagonal-neighbors", diagonal_neighbors,
+                NULL);
+}
+CODE
+    );
+}
+
+sub context_get_interpolation {
+    $blurb = 'Get the interpolation type.';
+
+    $help = <<'HELP';
+This procedure returns the interpolation setting. The return value
+is an integer which corresponds to the values listed in the argument
+description. If the interpolation has not been set explicitly by
+gimp_context_set_interpolation(), the default interpolation set in
+gimprc will be used.
+HELP
+
+    &mitch_pdb_misc('2010', '2.8');
+
+    @outargs = (
+        { name => 'interpolation', type => 'enum GimpInterpolationType',
+          desc => 'The interpolation type' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  g_object_get (context,
+                "interpolation", &interpolation,
+                NULL);
+}
+CODE
+    );
+}
+
+sub context_set_interpolation {
+    $blurb = 'Set the interpolation type.';
+
+    $help = <<'HELP';
+This procedure modifies the interpolation setting.
+
+This setting affects affects the following procedures:
+gimp_item_transform_flip(), gimp_item_transform_perspective(),
+gimp_item_transform_rotate(), gimp_item_transform_scale(),
+gimp_item_transform_shear(), gimp_item_transform_2d(),
+gimp_item_transform_matrix(), gimp_image_scale(), gimp_layer_scale().
+HELP
+
+    &mitch_pdb_misc('2010', '2.8');
+
+    @inargs = (
+        { name => 'interpolation', type => 'enum GimpInterpolationType',
+          desc => 'The interpolation type' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  g_object_set (context,
+                "interpolation", interpolation,
+                NULL);
+}
+CODE
+    );
+}
+
+sub context_get_transform_direction {
+    $blurb = 'Get the transform direction.';
+
+    $help = <<'HELP';
+This procedure returns the transform direction. The return value
+is an integer which corresponds to the values listed in the argument
+description.
+HELP
+
+    &mitch_pdb_misc('2010', '2.8');
+
+    @outargs = (
+        { name => 'transform_direction', type => 'enum GimpTransformDirection',
+          desc => 'The transform direction' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  g_object_get (context,
+                "transform-direction", &transform_direction,
+                NULL);
+}
+CODE
+    );
+}
+
+sub context_set_transform_direction {
+    $blurb = 'Set the transform direction.';
+
+    $help = <<'HELP';
+This procedure modifies the transform direction setting.
+
+This setting affects affects the following procedures:
+gimp_item_transform_flip(), gimp_item_transform_perspective(),
+gimp_item_transform_rotate(), gimp_item_transform_scale(),
+gimp_item_transform_shear(), gimp_item_transform_2d(),
+gimp_item_transform_matrix().
+HELP
+
+    &mitch_pdb_misc('2010', '2.8');
+
+    @inargs = (
+        { name => 'transform_direction', type => 'enum GimpTransformDirection',
+          desc => 'The transform direction' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  g_object_set (context,
+                "transform-direction", transform_direction,
+                NULL);
+}
+CODE
+    );
+}
+
+sub context_get_transform_resize {
+    $blurb = 'Get the transform resize type.';
+
+    $help = <<'HELP';
+This procedure returns the transform resize setting. The return value
+is an integer which corresponds to the values listed in the argument
+description.
+HELP
+
+    &mitch_pdb_misc('2010', '2.8');
+
+    @outargs = (
+        { name => 'transform_resize', type => 'enum GimpTransformResize',
+          desc => 'The transform resize type' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  g_object_get (context,
+                "transform-resize", &transform_resize,
+                NULL);
+}
+CODE
+    );
+}
+
+sub context_set_transform_resize {
+    $blurb = 'Set the transform resize type.';
+
+    $help = <<'HELP';
+This procedure modifies the transform resize setting. When transforming
+pixels, if the result of a transform operation  has a different size
+than the original area, this setting determines how the resulting area
+is sized.
+
+This setting affects affects the following procedures:
+gimp_item_transform_flip(), gimp_item_transform_flip_simple(),
+gimp_item_transform_perspective(), gimp_item_transform_rotate(),
+gimp_item_transform_rotate_simple(), gimp_item_transform_scale(),
+gimp_item_transform_shear(), gimp_item_transform_2d(),
+gimp_item_transform_matrix().
+HELP
+
+    &mitch_pdb_misc('2010', '2.8');
+
+    @inargs = (
+        { name => 'transform_resize', type => 'enum GimpTransformResize',
+          desc => 'The transform resize type' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  g_object_set (context,
+                "transform-resize", transform_resize,
+                NULL);
+}
+CODE
+    );
+}
+
+sub context_get_transform_recursion {
+    &std_pdb_deprecated();
+
+    @outargs = (
+        { name => 'transform_recursion', type => '1 <= int32',
+          desc => 'This returns always 3 and is meaningless' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  transform_recursion = 3;
+}
+CODE
+    );
+}
+
+sub context_set_transform_recursion {
+    &std_pdb_deprecated();
+
+    @inargs = (
+        { name => 'transform_recursion', type => '1 <= int32', dead => 1,
+          desc => 'This parameter is ignored' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+}
+CODE
+    );
+}
+
+sub context_get_ink_size {
+    $blurb = 'Get ink blob size in pixels.';
+    $help = 'Get the ink blob size in pixels for ink tool.';
+
+    &ejs_pdb_misc('2012', '2.8');
+
+    @outargs = (
+        { name => "size", type => "0 <= float <= 200",
+          desc => "ink blob size in pixels" }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-ink");
+
+  if (options)
+    g_object_get (options,
+                  "size", &size,
+                  NULL);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+sub context_set_ink_size {
+    $blurb = 'Set ink blob size in pixels.';
+    $help = 'Set the ink blob size in pixels for ink tool.';
+
+    &ejs_pdb_misc('2012', '2.8');
+
+    @inargs = (
+        { name => "size", type => "0 <= float <= 200",
+          desc => "ink blob size in pixels" }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-ink");
+
+  if (options)
+    g_object_set (options,
+                  "size", size,
+                  NULL);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+sub context_get_ink_angle {
+    $blurb = 'Get ink angle in degrees.';
+    $help = 'Get the ink angle in degrees for ink tool.';
+
+    &ejs_pdb_misc('2012', '2.8');
+
+    @outargs = (
+        { name => "angle", type => "-90 <= float <= 90",
+          desc => "ink angle in degrees" }
+    );
+
+    %invoke = (
+    code => <<'CODE'
+{
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-ink");
+
+  if (options)
+    g_object_get (options,
+                  "tilt-angle", &angle,
+                  NULL);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+sub context_set_ink_angle {
+    $blurb = 'Set ink angle in degrees.';
+    $help = 'Set the ink angle in degrees for ink tool.';
+
+    &ejs_pdb_misc('2012', '2.8');
+
+    @inargs = (
+        { name => "angle", type => "-90 <= float <= 90",
+          desc => "ink angle in degrees" }
+    );
+
+    %invoke = (
+    code => <<'CODE'
+{
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-ink");
+
+  if (options)
+    g_object_set (options,
+                  "tilt-angle", angle,
+                  NULL);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+sub context_get_ink_size_sensitivity {
+    $blurb = 'Get ink size sensitivity.';
+    $help = 'Get the ink size sensitivity for ink tool.';
+
+    &ejs_pdb_misc('2012', '2.8');
+
+    @outargs = (
+        { name => "size", type => "0 <= float <= 1",
+          desc => "ink size sensitivity" }
+    );
+
+    %invoke = (
+    code => <<'CODE'
+{
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-ink");
+
+  if (options)
+    g_object_get (options,
+                  "size-sensitivity", &size,
+                  NULL);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+sub context_set_ink_size_sensitivity {
+    $blurb = 'Set ink size sensitivity.';
+    $help = 'Set the ink size sensitivity for ink tool.';
+
+    &ejs_pdb_misc('2012', '2.8');
+
+    @inargs = (
+        { name => "size", type => "0 <= float <= 1",
+          desc => "ink size sensitivity" }
+    );
+
+    %invoke = (
+    code => <<'CODE'
+{
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-ink");
+
+  if (options)
+    g_object_set (options,
+                  "size-sensitivity", size,
+                  NULL);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+sub context_get_ink_tilt_sensitivity {
+    $blurb = 'Get ink tilt sensitivity.';
+    $help = 'Get the ink tilt sensitivity for ink tool.';
+
+    &ejs_pdb_misc('2012', '2.8');
+
+    @outargs = (
+        { name => "tilt", type => "0 <= float <= 1",
+          desc => "ink tilt sensitivity" }
+    );
+
+    %invoke = (
+    code => <<'CODE'
+{
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-ink");
+
+  if (options)
+    g_object_get (options,
+                  "tilt-sensitivity", &tilt,
+                  NULL);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+sub context_set_ink_tilt_sensitivity {
+    $blurb = 'Set ink tilt sensitivity.';
+    $help = 'Set the ink tilt sensitivity for ink tool.';
+
+    &ejs_pdb_misc('2012', '2.8');
+
+    @inargs = (
+        { name => "tilt", type => "0 <= float <= 1",
+          desc => "ink tilt sensitivity" }
+    );
+
+    %invoke = (
+    code => <<'CODE'
+{
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-ink");
+
+  if (options)
+    g_object_set (options,
+                  "tilt-sensitivity", tilt,
+                  NULL);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+sub context_get_ink_speed_sensitivity {
+    $blurb = 'Get ink speed sensitivity.';
+    $help = 'Get the ink speed sensitivity for ink tool.';
+
+    &ejs_pdb_misc('2012', '2.8');
+
+    @outargs = (
+        { name => "speed", type => "0 <= float <= 1",
+          desc => "ink speed sensitivity" }
+    );
+
+    %invoke = (
+    code => <<'CODE'
+{
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-ink");
+
+  if (options)
+    g_object_get (options,
+                  "vel-sensitivity", &speed,
+                  NULL);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+sub context_set_ink_speed_sensitivity {
+    $blurb = 'Set ink speed sensitivity.';
+    $help = 'Set the ink speed sensitivity for ink tool.';
+
+    &ejs_pdb_misc('2012', '2.8');
+
+    @inargs = (
+        { name => "speed", type => "0 <= float <= 1",
+          desc => "ink speed sensitivity" }
+    );
+
+    %invoke = (
+    code => <<'CODE'
+{
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-ink");
+
+  if (options)
+    g_object_set (options,
+                  "vel-sensitivity", speed,
+                  NULL);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+sub context_get_ink_blob_type {
+    $blurb = 'Get ink blob type.';
+    $help = 'Get the ink blob type for ink tool.';
+
+    &ejs_pdb_misc('2012', '2.8');
+
+    @outargs = (
+        { name => "type", type => "enum GimpInkBlobType",
+          desc => "Ink blob type" }
+    );
+
+    %invoke = (
+    code => <<'CODE'
+{
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-ink");
+
+  if (options)
+    g_object_get (options,
+                  "blob-type", &type,
+                  NULL);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+sub context_set_ink_blob_type {
+    $blurb = 'Set ink blob type.';
+    $help = 'Set the ink blob type for ink tool.';
+
+    &ejs_pdb_misc('2012', '2.8');
+
+    @inargs = (
+        { name => "type", type => "enum GimpInkBlobType",
+          desc => "Ink blob type" }
+    );
+
+    %invoke = (
+    code => <<'CODE'
+{
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-ink");
+
+  if (options)
+    g_object_set (options,
+                  "blob-type", type,
+                  NULL);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+sub context_get_ink_blob_aspect_ratio {
+    $blurb = 'Get ink blob aspect ratio.';
+    $help = 'Get the ink blob aspect ratio for ink tool.';
+
+    &ejs_pdb_misc('2012', '2.8');
+
+    @outargs = (
+        { name => "aspect", type => "1 <= float <= 10",
+          desc => "ink blob aspect ratio" }
+    );
+
+    %invoke = (
+    code => <<'CODE'
+{
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-ink");
+
+  if (options)
+    g_object_get (options,
+                  "blob-aspect", &aspect,
+                  NULL);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+sub context_set_ink_blob_aspect_ratio {
+    $blurb = 'Set ink blob aspect ratio.';
+    $help = 'Set the ink blob aspect ratio for ink tool.';
+
+    &ejs_pdb_misc('2012', '2.8');
+
+    @inargs = (
+        { name => "aspect", type => "1 <= float <= 10",
+          desc => "ink blob aspect ratio" }
+    );
+
+    %invoke = (
+    code => <<'CODE'
+{
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-ink");
+
+  if (options)
+    g_object_set (options,
+                  "blob-aspect", aspect,
+                  NULL);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+sub context_get_ink_blob_angle {
+    $blurb = 'Get ink blob angle in degrees.';
+    $help = 'Get the ink blob angle in degrees for ink tool.';
+
+    &ejs_pdb_misc('2012', '2.8');
+
+    @outargs = (
+        { name => "angle", type => "-180 <= float <= 180",
+          desc => "ink blob angle in degrees" }
+    );
+
+    %invoke = (
+    code => <<'CODE'
+{
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-ink");
+
+  if (options)
+    {
+      g_object_get (options,
+                   "blob-angle", &angle,
+                   NULL);
+      angle *= (180-0 / G_PI);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+sub context_set_ink_blob_angle {
+    $blurb = 'Set ink blob angle in degrees.';
+    $help = 'Set the ink blob angle in degrees for ink tool.';
+
+    &ejs_pdb_misc('2012', '2.8');
+
+    @inargs = (
+        { name => "angle", type => "-180 <= float <= 180",
+          desc => "ink blob angle in degrees" }
+    );
+
+    %invoke = (
+    code => <<'CODE'
+{
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-ink");
+
+  if (options)
+    g_object_set (options,
+                  "blob-angle", (gdouble) angle * G_PI / 180.0,
+                  NULL);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+@headers = qw("core/gimp.h"
+              "core/gimpcontainer.h"
+              "core/gimpdashpattern.h"
+              "core/gimpdatafactory.h"
+              "core/gimpstrokeoptions.h"
+              "paint/gimppaintoptions.h"
+              "libgimpconfig/gimpconfig.h"
+              "plug-in/gimpplugin.h"
+              "plug-in/gimpplugin-context.h"
+              "plug-in/gimppluginmanager.h"
+              "gimppdb-utils.h"
+              "gimppdbcontext.h");
+
+@procs = qw(context_push context_pop context_set_defaults
+            context_list_paint_methods
+            context_get_paint_method context_set_paint_method
+            context_get_stroke_method context_set_stroke_method
+            context_get_foreground context_set_foreground
+            context_get_background context_set_background
+            context_set_default_colors
+            context_swap_colors
+            context_get_opacity context_set_opacity
+            context_get_paint_mode context_set_paint_mode
+            context_get_line_width context_set_line_width
+            context_get_line_width_unit context_set_line_width_unit
+            context_get_line_cap_style context_set_line_cap_style
+            context_get_line_join_style context_set_line_join_style
+            context_get_line_miter_limit context_set_line_miter_limit
+            context_get_line_dash_offset context_set_line_dash_offset
+            context_get_line_dash_pattern context_set_line_dash_pattern
+            context_get_brush context_set_brush
+            context_get_brush_size
+            context_set_brush_size context_set_brush_default_size
+            context_get_brush_aspect_ratio context_set_brush_aspect_ratio
+            context_get_brush_angle context_set_brush_angle
+            context_get_brush_spacing
+            context_set_brush_spacing context_set_brush_default_spacing
+            context_get_brush_hardness
+            context_set_brush_hardness context_set_brush_default_hardness
+            context_get_brush_force
+            context_set_brush_force
+            context_get_dynamics context_set_dynamics
+            context_get_mypaint_brush context_set_mypaint_brush
+            context_get_pattern context_set_pattern
+            context_get_gradient context_set_gradient
+            context_get_palette context_set_palette
+            context_get_font context_set_font
+            context_get_antialias context_set_antialias
+            context_get_feather context_set_feather
+            context_get_feather_radius context_set_feather_radius
+            context_get_sample_merged context_set_sample_merged
+            context_get_sample_criterion context_set_sample_criterion
+            context_get_sample_threshold context_set_sample_threshold
+            context_get_sample_threshold_int context_set_sample_threshold_int
+            context_get_sample_transparent context_set_sample_transparent
+            context_get_diagonal_neighbors context_set_diagonal_neighbors
+            context_get_interpolation context_set_interpolation
+            context_get_transform_direction context_set_transform_direction
+            context_get_transform_resize context_set_transform_resize
+            context_get_transform_recursion context_set_transform_recursion
+            context_get_ink_size context_set_ink_size
+            context_get_ink_angle context_set_ink_angle
+            context_get_ink_size_sensitivity context_set_ink_size_sensitivity
+            context_get_ink_tilt_sensitivity context_set_ink_tilt_sensitivity
+            context_get_ink_speed_sensitivity context_set_ink_speed_sensitivity
+            context_get_ink_blob_type context_set_ink_blob_type
+            context_get_ink_blob_aspect_ratio context_set_ink_blob_aspect_ratio
+            context_get_ink_blob_angle context_set_ink_blob_angle);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Context';
+$doc_title = 'gimpcontext';
+$doc_short_desc = "Functions to manipulate a plug-in's context.";
+$doc_long_desc = "Functions to manipulate a plug-in's context.";
+
+1;
diff --git a/pdb/groups/debug.pdb b/pdb/groups/debug.pdb
new file mode 100644
index 0000000..829c799
--- /dev/null
+++ b/pdb/groups/debug.pdb
@@ -0,0 +1,104 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub debug_timer_start {
+    $blurb = 'Starts measuring elapsed time.';
+
+    $help = <<'HELP';
+This procedure starts a timer, measuring the elapsed time since the call.
+Each call to this procedure should be matched by a call to
+gimp_debug_timer_end(), which returns the elapsed time.
+
+If there is already an active timer, it is not affected by the call, however, a
+matching gimp_debug_timer_end() call is still required.
+HELP
+
+    &ell_pdb_misc('2017');
+
+    &std_pdb_debug();
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_debug_timer_counter++ == 0)
+    gimp_debug_timer = g_timer_new ();
+}
+CODE
+    );
+}
+
+sub debug_timer_end {
+    $blurb = 'Finishes measuring elapsed time.';
+
+    $help = <<'HELP';
+This procedure stops the timer started by a previous gimp_debug_timer_start()
+call, and prints and returns the elapsed time.
+
+If there was already an active timer at the time of corresponding call to
+gimp_debug_timer_start(), a dummy value is returned.
+HELP
+
+    &ell_pdb_misc('2017');
+
+    &std_pdb_debug();
+
+    @outargs = (
+        { name => 'elapsed', type => 'float',
+          desc => 'The elapsed time, in seconds' }
+    );
+
+
+    %invoke = (
+       code => <<'CODE'
+{
+  elapsed = 0.0;
+
+  if (gimp_debug_timer_counter == 0)
+    success = FALSE;
+  else if (--gimp_debug_timer_counter == 0)
+    {
+      elapsed = g_timer_elapsed (gimp_debug_timer, NULL);
+
+      g_printerr ("GIMP debug timer: %g seconds\n", elapsed);
+
+      g_timer_destroy (gimp_debug_timer);
+
+      gimp_debug_timer = NULL;
+    }
+}
+CODE
+    );
+}
+
+
+$extra{app}->{code} = <<'CODE';
+static GTimer *gimp_debug_timer         = NULL;
+static gint    gimp_debug_timer_counter = 0;
+CODE
+
+
+@procs = qw(debug_timer_start debug_timer_end);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Debug';
+$doc_title = 'gimpdebug';
+$doc_short_desc = 'Debug utility functions';
+$doc_long_desc = 'Miscellaneous debug utility functions. Not part of the stable library interface.';
+
+1;
diff --git a/pdb/groups/display.pdb b/pdb/groups/display.pdb
new file mode 100644
index 0000000..3ee6124
--- /dev/null
+++ b/pdb/groups/display.pdb
@@ -0,0 +1,229 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub display_is_valid {
+    $blurb = 'Returns TRUE if the display is valid.';
+
+    $help = <<'HELP';
+This procedure checks if the given display ID is valid and refers to an
+existing display.
+HELP
+
+    &neo_pdb_misc('2007', '2.4');
+
+    @inargs = (
+       { name => 'display', type => 'display', no_validate => 1,
+         desc => 'The display to check' }
+    );
+
+    @outargs = (
+       { name => 'valid', type => 'boolean',
+         desc => 'Whether the display ID is valid' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  valid = (display != NULL);
+}
+CODE
+    );
+}
+
+sub display_new {
+    $blurb = 'Create a new display for the specified image.';
+
+    $help = <<'HELP';
+Creates a new display for the specified image. If the image already has a
+display, another is added. Multiple displays are handled transparently by
+GIMP. The newly created display is returned and can be subsequently destroyed
+with a call to gimp_display_delete(). This procedure only makes sense for use
+with the GIMP UI, and will result in an execution error if called when
+GIMP has no UI.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' }
+    );
+
+    @outargs = (
+       { name => 'display', type => 'display',
+         desc => 'The new display' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_image_flush (image);
+
+  display = gimp_create_display (gimp, image, GIMP_UNIT_PIXEL, 1.0, NULL, 0);
+
+  if (display)
+    {
+      /* the first display takes ownership of the image */
+      if (gimp_image_get_display_count (image) == 1)
+        g_object_unref (image);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub display_delete {
+    $blurb = 'Delete the specified display.';
+
+    $help = <<'HELP';
+This procedure removes the specified display. If this is the last remaining 
+display for the underlying image, then the image is deleted also. Note that
+the display is closed no matter if the image is dirty or not. Better save
+the image before calling this procedure.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'display', type => 'display',
+         desc => 'The display to delete' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_delete_display (gimp, display);
+}
+CODE
+    );
+}
+
+sub display_get_window_handle {
+    $blurb = 'Get a handle to the native window for an image display.';
+
+    $help = <<'HELP';
+This procedure returns a handle to the native window for a given image
+display. For example in the X backend of GDK, a native window handle is
+an Xlib XID. A value of 0 is returned for an invalid display or if this
+function is unimplemented for the windowing system that is being used.
+HELP
+
+    &neo_pdb_misc('2005', '2.4');
+
+    @inargs = (
+       { name => 'display', type => 'display',
+         desc => 'The display to get the window handle from' }
+    );
+
+    @outargs = (
+        { name => 'window', type => 'int32',
+          desc => 'The native window handle or 0' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  window = (gint32) gimp_get_display_window_id (gimp, display);
+}
+CODE
+    );
+}
+
+sub displays_flush {
+    $blurb = 'Flush all internal changes to the user interface';
+
+    $help = <<'HELP';
+This procedure takes no arguments and returns nothing except a success status.
+Its purpose is to flush all pending updates of image manipulations to the user
+interface. It should be called whenever appropriate.
+HELP
+
+    &std_pdb_misc;
+
+    %invoke = (
+        headers => [ qw("core/gimpcontainer.h") ],
+        code    => <<'CODE'
+
+{
+  gimp_container_foreach (gimp->images, (GFunc) gimp_image_flush, NULL);
+}
+CODE
+    );
+}
+
+sub displays_reconnect {
+    $blurb = 'Reconnect displays from one image to another image.';
+
+    $help = <<'HELP';
+This procedure connects all displays of the old_image to the
+new_image.  If the old_image has no display or new_image already has a
+display the reconnect is not performed and the procedure returns
+without success.  You should rarely need to use this function.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = ( 
+       { name => 'old_image', type => 'image',
+         desc => 'The old image (must have at least one display)' },
+       { name => 'new_image', type => 'image',
+         desc => 'The new image (must not have a display)' }
+     );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  success = (old_image != new_image    &&
+             gimp_image_get_display_count (old_image) > 0 &&
+             gimp_image_get_display_count (new_image) == 0);
+
+  if (success)
+    {
+      gimp_reconnect_displays (gimp, old_image, new_image);
+
+      /* take ownership of the image */
+      if (gimp_image_get_display_count (new_image) > 0)
+        g_object_unref (new_image);
+    }
+}
+CODE
+    );
+}
+
+
+@headers = qw("core/gimp.h");
+
+@procs = qw(display_is_valid
+            display_new
+            display_delete
+            display_get_window_handle
+           displays_flush
+            displays_reconnect);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Display procedures';
+$doc_title = 'gimpdisplay';
+$doc_short_desc = 'Functions to create, delete and flush displays (views) on an image.';
+$doc_long_desc = 'Functions to create, delete and flush displays (views) on an image.';
+
+1;
diff --git a/pdb/groups/drawable.pdb b/pdb/groups/drawable.pdb
new file mode 100644
index 0000000..a794ffb
--- /dev/null
+++ b/pdb/groups/drawable.pdb
@@ -0,0 +1,1035 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub drawable_merge_shadow {
+    $blurb = 'Merge the shadow buffer with the specified drawable.';
+
+    $help = <<'HELP';
+This procedure combines the contents of the drawable's shadow buffer
+(for temporary processing) with the specified drawable. The 'undo'
+parameter specifies whether to add an undo step for the operation.
+Requesting no undo is useful for such applications as 'auto-apply'.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' },
+        { name => 'undo', type => 'boolean',
+         desc => 'Push merge to undo stack?' }
+    );
+
+    %invoke = (
+       headers => [ qw("core/gimpdrawable-shadow.h"
+                        "plug-in/gimpplugin.h"
+                        "plug-in/gimppluginmanager.h") ],
+       code    => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      const gchar *undo_desc = _("Plug-in");
+
+      if (gimp->plug_in_manager->current_plug_in)
+        undo_desc = gimp_plug_in_get_undo_desc (gimp->plug_in_manager->current_plug_in);
+
+      gimp_drawable_merge_shadow_buffer (drawable, undo, undo_desc);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub drawable_free_shadow {
+    $blurb = "Free the specified drawable's shadow data (if it exists).";
+
+    $help = <<'HELP';
+This procedure is intended as a memory saving device. If any shadow
+memory has been allocated, it will be freed automatically when the
+drawable is removed from the image, or when the plug-in procedure
+which allocated it returns.
+HELP
+
+    &mitch_pdb_misc('2008', '2.6');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' }
+    );
+
+    %invoke = (
+       headers => [ qw("plug-in/gimpplugin-cleanup.h") ],
+       code => <<'CODE'
+{
+  if (gimp->plug_in_manager->current_plug_in)
+    gimp_plug_in_cleanup_remove_shadow (gimp->plug_in_manager->current_plug_in,
+                                        drawable);
+
+  gimp_drawable_free_shadow_buffer (drawable);
+}
+CODE
+    );
+}
+
+sub drawable_fill {
+    $blurb = 'Fill the drawable with the specified fill mode.';
+
+    $help = <<'HELP';
+This procedure fills the drawable. If the fill mode is foreground the
+current foreground color is used. If the fill mode is background, the
+current background color is used. If the fill type is white, then
+white is used. Transparent fill only affects layers with an alpha
+channel, in which case the alpha channel is set to transparent. If the
+drawable has no alpha channel, it is filled to white. No fill leaves
+the drawable's contents undefined.
+
+This procedure is unlike gimp_edit_fill() or the bucket fill tool
+because it fills regardless of a selection. Its main purpose is to
+fill a newly created drawable before adding it to the image. This
+operation cannot be undone.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' },
+       { name => 'fill_type', type => 'enum GimpFillType',
+         desc => 'The type of fill' }
+    );
+
+    %invoke = (
+       headers => [ qw("core/gimpdrawable-fill.h") ],
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_modifyable (GIMP_ITEM (drawable),
+                                   GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      gimp_drawable_fill (drawable, context, (GimpFillType) fill_type);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub drawable_update {
+    $blurb = 'Update the specified region of the drawable.';
+
+    $help = <<'HELP';
+This procedure updates the specified region of the drawable. The (x, y)
+coordinate pair is relative to the drawable's origin, not to the image origin.
+Therefore, the entire drawable can be updated using (0, 0, width, height).
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' },
+       { name => 'x', type => 'int32',
+         desc => 'x coordinate of upper left corner of update region' },
+       { name => 'y', type => 'int32',
+         desc => 'y coordinate of upper left corner of update region' },
+       { name => 'width', type => 'int32',
+         desc => 'Width of update region' },
+       { name => 'height', type => 'int32',
+         desc => 'Height of update region' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_drawable_update (drawable, x, y, width, height);
+}
+CODE
+    );
+}
+
+sub drawable_mask_bounds {
+    $blurb = <<'BLURB';
+Find the bounding box of the current selection in relation to the specified
+drawable.
+BLURB
+
+    $help = <<'HELP';
+This procedure returns whether there is a selection. If there is one, the
+upper left and lower right-hand corners of its bounding box are returned. These
+coordinates are specified relative to the drawable's origin, and bounded by
+the drawable's extents. Please note that the pixel specified by the lower
+right-hand coordinate of the bounding box is not part of the selection. The
+selection ends at the upper left corner of this pixel. This means the width
+of the selection can be calculated as (x2 - x1), its height as (y2 - y1).
+
+Note that the returned boolean does NOT correspond with the returned
+region being empty or not, it always returns whether the selection
+is non_empty. See gimp_drawable_mask_intersect() for a boolean
+return value which is more useful in most cases.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' }
+    );
+
+    @outargs = (
+       { name => 'non_empty', type => 'boolean',
+         desc => 'TRUE if there is a selection' },
+       { name => 'x1', type => 'int32',
+         desc => "x coordinate of the upper left corner of selection bounds" },
+       { name => 'y1', type => 'int32',
+         desc => "y coordinate of the upper left corner of selection bounds" },
+       { name => 'x2', type => 'int32',
+         desc => "x coordinate of the lower right corner of selection bounds" },
+       { name => 'y2', type => 'int32',
+         desc => "y coordinate of the lower right corner of selection bounds" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL, 0, error))
+    non_empty = gimp_item_mask_bounds (GIMP_ITEM (drawable), &x1, &y1, &x2, &y2);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub drawable_mask_intersect {
+    $blurb = <<'BLURB';
+Find the bounding box of the current selection in relation to the specified
+drawable.
+BLURB
+
+    $help = <<'HELP';
+This procedure returns whether there is an intersection between the
+drawable and the selection. Unlike gimp_drawable_mask_bounds(), the
+intersection's bounds are returned as x, y, width, height.
+
+If there is no selection this function returns TRUE and the returned
+bounds are the extents of the whole drawable.
+HELP
+
+    &mitch_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' }
+    );
+
+    @outargs = (
+       { name => 'non_empty', type => 'boolean',
+         desc => 'TRUE if the returned area is not empty' },
+       { name => 'x', type => 'int32',
+          desc => 'x coordinate of the upper left corner of the intersection' },
+       { name => 'y', type => 'int32',
+          desc => 'y coordinate of the upper left corner of the intersection' },
+       { name => 'width', type => 'int32',
+          desc => 'width of the intersection' },
+       { name => 'height', type => 'int32',
+          desc => 'height of the intersection' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL, 0, error))
+    non_empty = gimp_item_mask_intersect (GIMP_ITEM (drawable),
+                                          &x, &y, &width, &height);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub drawable_get_format {
+    $blurb = "Returns the drawable's Babl format";
+    $help  = "This procedure returns the drawable's Babl format.";
+
+    &mitch_pdb_misc('2012', '2.10');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' }
+    );
+
+    @outargs = (
+       { name => 'format', type => 'string', wrap => 1,
+         desc => "The drawable's Babl format" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp->plug_in_manager->current_plug_in)
+    gimp_plug_in_enable_precision (gimp->plug_in_manager->current_plug_in);
+
+  format = g_strdup (babl_get_name (gimp_drawable_get_format (drawable)));
+}
+CODE
+    );
+}
+
+sub drawable_type {
+    $blurb = "Returns the drawable's type.";
+    $help  = "This procedure returns the drawable's type.";
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' }
+    );
+
+    @outargs = (
+       { name => 'type', type => 'enum GimpImageType',
+         desc => "The drawable's type" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  type = gimp_babl_format_get_image_type (gimp_drawable_get_format (drawable));
+}
+CODE
+    );
+}
+
+sub drawable_has_alpha {
+    $blurb = 'Returns TRUE if the drawable has an alpha channel.';
+
+    $help = <<'HELP';
+This procedure returns whether the specified drawable has an alpha channel.
+This can only be true for layers, and the associated type will be one of:
+{ RGBA , GRAYA, INDEXEDA }.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' }
+    );
+
+    @outargs = (
+       { name => 'has_alpha', type => 'boolean',
+         desc => 'Does the drawable have an alpha channel?' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  has_alpha = gimp_drawable_has_alpha (drawable);
+}
+CODE
+    );
+}
+
+sub drawable_type_with_alpha {
+    $blurb = "Returns the drawable's type with alpha.";
+
+    $help = <<'HELP';
+This procedure returns the drawable's type as if had an alpha
+channel. If the type is currently Gray, for instance, the returned
+type would be GrayA. If the drawable already has an alpha channel, the
+drawable's type is simply returned.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' }
+    );
+
+    @outargs = (
+       { name => 'type_with_alpha', type => 'enum GimpImageType
+                                             (no GIMP_RGB_IMAGE,
+                                              GIMP_GRAY_IMAGE,
+                                              GIMP_INDEXED_IMAGE)',
+         desc => "The drawable's type with alpha" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  const Babl *format = gimp_drawable_get_format_with_alpha (drawable);
+
+  type_with_alpha = gimp_babl_format_get_image_type (format);
+}
+CODE
+    );
+}
+
+sub drawable_is_rgb {
+    $blurb = 'Returns whether the drawable is an RGB type.';
+
+    $help = <<HELP;
+This procedure returns TRUE if the specified drawable
+is of type { RGB, RGBA }.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' }
+    );
+
+    @outargs = (
+       { name => 'is_rgb', type => 'boolean',
+         desc => 'TRUE if the drawable is an RGB type' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  is_rgb = gimp_drawable_is_rgb (drawable);
+}
+CODE
+    );
+}
+
+sub drawable_is_gray {
+    $blurb = 'Returns whether the drawable is a grayscale type.';
+
+    $help = <<HELP;
+This procedure returns TRUE if the specified drawable
+is of type { Gray, GrayA }.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' }
+    );
+
+    @outargs = (
+       { name => 'is_gray', type => 'boolean',
+         desc => 'TRUE if the drawable is a grayscale type' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  is_gray = gimp_drawable_is_gray (drawable);
+}
+CODE
+    );
+}
+
+sub drawable_is_indexed {
+    $blurb = 'Returns whether the drawable is an indexed type.';
+
+    $help = <<HELP;
+This procedure returns TRUE if the specified drawable
+is of type { Indexed, IndexedA }.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' }
+    );
+
+    @outargs = (
+       { name => 'is_indexed', type => 'boolean',
+         desc => 'TRUE if the drawable is an indexed type' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  is_indexed = gimp_drawable_is_indexed (drawable);
+}
+CODE
+    );
+}
+
+sub drawable_bpp {
+    $blurb = 'Returns the bytes per pixel.';
+
+    $help = <<'HELP';
+This procedure returns the number of bytes per pixel, which corresponds to
+the number of components unless gimp_plugin_enable_precision() was called.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' }
+    );
+
+    @outargs = (
+       { name => 'bpp', type => 'int32',
+         desc => 'Bytes per pixel' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  const Babl *format = gimp_drawable_get_format (drawable);
+
+  if (! gimp->plug_in_manager->current_plug_in ||
+      ! gimp_plug_in_precision_enabled (gimp->plug_in_manager->current_plug_in))
+    {
+      format = gimp_babl_compat_u8_format (format);
+    }
+
+  bpp = babl_format_get_bytes_per_pixel (format);
+}
+CODE
+    );
+}
+
+sub drawable_width {
+    $blurb = 'Returns the width of the drawable.';
+    $help  = "This procedure returns the specified drawable's width in pixels.";
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' }
+    );
+
+    @outargs = (
+       { name => 'width', type => 'int32',
+         desc => 'Width of drawable' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  width = gimp_item_get_width (GIMP_ITEM (drawable));
+}
+CODE
+    );
+}
+
+sub drawable_height {
+    $blurb = 'Returns the height of the drawable.';
+    $help  = "This procedure returns the specified drawable's height in pixels.";
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' }
+    );
+
+    @outargs = (
+       { name => 'height', type => 'int32',
+         desc => 'Height of drawable' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  height = gimp_item_get_height (GIMP_ITEM (drawable));
+}
+CODE
+    );
+}
+
+sub drawable_offsets {
+    $blurb = 'Returns the offsets for the drawable.';
+
+    $help = <<'HELP';
+This procedure returns the specified drawable's offsets. This only makes sense
+if the drawable is a layer since channels are anchored. The offsets of a
+channel will be returned as 0.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' }
+    );
+
+    @outargs = (
+       { name => 'offset_x', type => 'int32', void_ret => 1,
+         desc => "x offset of drawable" },
+       { name => 'offset_y', type => 'int32',
+         desc => "y offset of drawable" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_item_get_offset (GIMP_ITEM (drawable), &offset_x, &offset_y);
+}
+CODE
+    );
+}
+
+sub drawable_get_pixel {
+    $blurb = 'Gets the value of the pixel at the specified coordinates.';
+
+    $help = <<'HELP';
+This procedure gets the pixel value at the specified coordinates. The
+'num_channels' argument must always be equal to the bytes-per-pixel value for
+the specified drawable.
+HELP
+
+    &std_pdb_misc;
+    $date = '1997';
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' },
+       { name => 'x_coord', type => '0 <= int32',
+         desc => 'The x coordinate' },
+       { name => 'y_coord', type => '0 <= int32',
+         desc => 'The y coordinate' }
+    );
+
+    @outargs = (
+       { name => 'pixel', type => 'int8array',
+         desc => 'The pixel value',
+         array => { name => 'num_channels', no_validate => 1,
+                    desc => 'The number of channels for the pixel' } }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  const Babl *format = gimp_drawable_get_format (drawable);
+
+  if (! gimp->plug_in_manager->current_plug_in ||
+      ! gimp_plug_in_precision_enabled (gimp->plug_in_manager->current_plug_in))
+    {
+      format = gimp_babl_compat_u8_format (format);
+    }
+
+  if (x_coord < gimp_item_get_width  (GIMP_ITEM (drawable)) &&
+      y_coord < gimp_item_get_height (GIMP_ITEM (drawable)))
+    {
+      num_channels = babl_format_get_bytes_per_pixel (format);
+      pixel = g_new0 (guint8, num_channels);
+
+      gegl_buffer_sample (gimp_drawable_get_buffer (drawable),
+                          x_coord, y_coord, NULL, pixel, format,
+                          GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub drawable_set_pixel {
+    $blurb = 'Sets the value of the pixel at the specified coordinates.';
+
+    $help = <<'HELP';
+This procedure sets the pixel value at the specified coordinates. The
+'num_channels' argument must always be equal to the bytes-per-pixel value for
+the specified drawable. Note that this function is not undoable, you should
+use it only on drawables you just created yourself.
+HELP
+
+    &std_pdb_misc;
+    $date = '1997';
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' },
+       { name => 'x_coord', type => '0 <= int32',
+         desc => 'The x coordinate' },
+       { name => 'y_coord', type => '0 <= int32',
+         desc => 'The y coordinate' },
+       { name => 'pixel', type => 'int8array',
+         desc => 'The pixel value',
+         array => { name => 'num_channels', no_validate => 1,
+                    desc => 'The number of channels for the pixel' } }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  const Babl *format = gimp_drawable_get_format (drawable);
+
+  if (! gimp->plug_in_manager->current_plug_in ||
+      ! gimp_plug_in_precision_enabled (gimp->plug_in_manager->current_plug_in))
+    {
+      format = gimp_babl_compat_u8_format (format);
+    }
+
+  if (gimp_pdb_item_is_modifyable (GIMP_ITEM (drawable),
+                                   GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error) &&
+      x_coord < gimp_item_get_width  (GIMP_ITEM (drawable)) &&
+      y_coord < gimp_item_get_height (GIMP_ITEM (drawable)) &&
+      num_channels == babl_format_get_bytes_per_pixel (format))
+    {
+      gegl_buffer_set (gimp_drawable_get_buffer (drawable),
+                       GEGL_RECTANGLE (x_coord, y_coord, 1, 1),
+                       0, format, pixel, GEGL_AUTO_ROWSTRIDE);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub drawable_set_image {
+    &std_pdb_deprecated();
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' },
+       { name => 'image', type => 'image',
+         desc => 'The image' }
+    );
+
+    %invoke = (
+       code =><<'CODE'
+{
+  if (image != gimp_item_get_image (GIMP_ITEM (drawable)))
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub drawable_thumbnail {
+    $blurb = 'Get a thumbnail of a drawable.';
+
+    $help = <<'HELP';
+This function gets data from which a thumbnail of a drawable preview
+can be created. Maximum x or y dimension is 1024 pixels. The pixels are
+returned in RGB[A] or GRAY[A] format. The bpp return value gives the
+number of bytes in the image.
+HELP
+
+    &andy_pdb_misc('1999');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' },
+       { name => 'width', type => '1 <= int32 <= 1024',
+         desc => 'The requested thumbnail width' },
+       { name => 'height', type => '1 <= int32 <= 1024',
+         desc => 'The requested thumbnail height' }
+    );
+
+    @outargs = (
+       { name => 'actual_width', type => 'int32', void_ret => 1,
+         desc => 'The previews width' },
+       { name => 'actual_height', type => 'int32',
+         desc => 'The previews height' },
+       { name => 'bpp', type => 'int32',
+         desc => 'The previews bpp' },
+        { name => 'thumbnail_data', type => 'int8array',
+         desc => 'The thumbnail data', wrap => 1,
+         array => { name => 'thumbnail_data_count',
+                    desc => 'The number of bytes in thumbnail data' } }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpImage   *image = gimp_item_get_image (GIMP_ITEM (drawable));
+  GimpTempBuf *buf;
+  gint         dwidth, dheight;
+
+  g_assert (GIMP_VIEWABLE_MAX_PREVIEW_SIZE >= 1024);
+
+  /* Adjust the width/height ratio */
+  dwidth  = gimp_item_get_width  (GIMP_ITEM (drawable));
+  dheight = gimp_item_get_height (GIMP_ITEM (drawable));
+
+  if (dwidth > dheight)
+    height = MAX (1, (width * dheight) / dwidth);
+  else
+    width  = MAX (1, (height * dwidth) / dheight);
+
+  if (image->gimp->config->layer_previews)
+    buf = gimp_viewable_get_new_preview (GIMP_VIEWABLE (drawable), context,
+                                         width, height);
+  else
+    buf = gimp_viewable_get_dummy_preview (GIMP_VIEWABLE (drawable),
+                                           width, height,
+                                           gimp_drawable_get_preview_format (drawable));
+
+  if (buf)
+    {
+      actual_width         = gimp_temp_buf_get_width  (buf);
+      actual_height        = gimp_temp_buf_get_height (buf);
+      bpp                  = babl_format_get_bytes_per_pixel (gimp_temp_buf_get_format (buf));
+      thumbnail_data_count = gimp_temp_buf_get_data_size (buf);
+      thumbnail_data       = g_memdup (gimp_temp_buf_get_data (buf),
+                                       thumbnail_data_count);
+
+      gimp_temp_buf_unref (buf);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub drawable_sub_thumbnail {
+    $blurb = 'Get a thumbnail of a sub-area of a drawable drawable.';
+
+    $help = <<'HELP';
+This function gets data from which a thumbnail of a drawable preview
+can be created. Maximum x or y dimension is 1024 pixels. The pixels are
+returned in RGB[A] or GRAY[A] format. The bpp return value gives the
+number of bytes in the image.
+HELP
+
+    &mitch_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' },
+        { name => 'src_x', type => '0 <= int32',
+          desc => 'The x coordinate of the area' },
+        { name => 'src_y', type => '0 <= int32',
+          desc => 'The y coordinate of the area' },
+        { name => 'src_width', type => '1 <= int32',
+          desc => 'The width of the area' },
+        { name => 'src_height', type => '1 <= int32',
+          desc => 'The height of the area' },
+       { name => 'dest_width', type => '1 <= int32 <= 1024',
+         desc => 'The thumbnail width' },
+       { name => 'dest_height', type => '1 <= int32 <= 1024',
+         desc => 'The thumbnail height' }
+    );
+
+    @outargs = (
+       { name => 'width', type => 'int32', void_ret => 1,
+         desc => 'The previews width' },
+       { name => 'height', type => 'int32',
+         desc => 'The previews height' },
+       { name => 'bpp', type => 'int32',
+         desc => 'The previews bpp' },
+        { name => 'thumbnail_data', type => 'int8array',
+         desc => 'The thumbnail data', wrap => 1,
+         array => { name => 'thumbnail_data_count',
+                    desc => 'The number of bytes in thumbnail data' } }
+    );
+
+    %invoke = (
+       headers => [ qw("core/gimpdrawable-preview.h") ],
+        code    => <<'CODE'
+{
+  if ((src_x + src_width)  <= gimp_item_get_width  (GIMP_ITEM (drawable)) &&
+      (src_y + src_height) <= gimp_item_get_height (GIMP_ITEM (drawable)))
+    {
+      GimpImage   *image = gimp_item_get_image (GIMP_ITEM (drawable));
+      GimpTempBuf *buf;
+
+      if (image->gimp->config->layer_previews)
+        buf = gimp_drawable_get_sub_preview (drawable,
+                                             src_x, src_y,
+                                             src_width, src_height,
+                                             dest_width, dest_height);
+      else
+        buf = gimp_viewable_get_dummy_preview (GIMP_VIEWABLE (drawable),
+                                               dest_width, dest_height,
+                                               gimp_drawable_get_preview_format (drawable));
+
+      if (buf)
+        {
+          width                = gimp_temp_buf_get_width  (buf);
+          height               = gimp_temp_buf_get_height (buf);
+          bpp                  = babl_format_get_bytes_per_pixel (gimp_temp_buf_get_format (buf));
+          thumbnail_data_count = gimp_temp_buf_get_data_size (buf);
+          thumbnail_data       = g_memdup (gimp_temp_buf_get_data (buf),
+                                           thumbnail_data_count);
+
+          gimp_temp_buf_unref (buf);
+        }
+      else
+        success = FALSE;
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub drawable_offset {
+    $blurb = <<'BLURB';
+Offset the drawable by the specified amounts in the X and Y directions
+BLURB
+
+    $help = <<'HELP';
+This procedure offsets the specified drawable by the amounts specified by
+'offset_x' and 'offset_y'. If 'wrap_around' is set to TRUE, then portions of
+the drawable which are offset out of bounds are wrapped around. Alternatively,
+the undefined regions of the drawable can be filled with transparency or the
+background color, as specified by the 'fill-type' parameter.
+HELP
+
+    &std_pdb_misc;
+    $date = '1997';
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable to offset' },
+       { name => 'wrap_around', type => 'boolean',
+         desc => 'wrap image around or fill vacated regions' },
+       { name => 'fill_type', type => 'enum GimpOffsetType',
+         desc => 'fill vacated regions of drawable with background or
+                  transparent' },
+       { name => 'offset_x', type => 'int32',
+         desc => 'offset by this amount in X direction' },
+       { name => 'offset_y', type => 'int32',
+         desc => 'offset by this amount in Y direction' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    gimp_drawable_offset (drawable, context, wrap_around, fill_type,
+                          offset_x, offset_y);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub drawable_foreground_extract {
+    $blurb = 'Extract the foreground of a drawable using a given trimap.';
+
+    $help = <<'HELP';
+Image Segmentation by Uniform Color Clustering, see
+http://www.inf.fu-berlin.de/inst/pubs/tr-b-05-07.pdf
+HELP
+
+    $author    = 'Gerald Friedland <fland inf fu-berlin de>, Kristian Jantz <jantz inf fu-berlin de>, Sven 
Neumann <sven gimp org>';
+    $copyright = 'Gerald Friedland';
+    $date      = '2005';
+    $since     = '2.4';
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' },
+        { name => 'mode', type => 'enum GimpForegroundExtractMode',
+         desc => 'The algorithm to use' },
+        { name => 'mask', type => 'drawable', desc => 'Tri-Map' }
+    );
+
+    %invoke = (
+       headers => [ qw("core/gimpdrawable-foreground-extract.h") ],
+        code    => <<'CODE'
+{
+  if (mode == GIMP_FOREGROUND_EXTRACT_MATTING &&
+      gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL, 0, error))
+    {
+      GimpPDBContext *pdb_context = GIMP_PDB_CONTEXT (context);
+      GimpImage      *image       = gimp_item_get_image (GIMP_ITEM (drawable));
+      GeglBuffer     *buffer;
+
+      buffer = gimp_drawable_foreground_extract (drawable,
+                                                 GIMP_MATTING_ENGINE_GLOBAL,
+                                                 2,
+                                                 2,
+                                                 2,
+                                                 gimp_drawable_get_buffer (mask),
+                                                 progress);
+
+      gimp_channel_select_buffer (gimp_image_get_mask (image),
+                                  C_("command", "Foreground Select"),
+                                  buffer,
+                                  0, /* x offset */
+                                  0, /* y offset */
+                                  GIMP_CHANNEL_OP_REPLACE,
+                                  pdb_context->feather,
+                                  pdb_context->feather_radius_x,
+                                  pdb_context->feather_radius_y);
+
+      g_object_unref (buffer);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+@headers = qw("config/gimpcoreconfig.h"
+              "gegl/gimp-babl.h"
+              "gegl/gimp-babl-compat.h"
+              "core/gimp.h"
+              "core/gimpchannel-select.h"
+              "core/gimpdrawable-offset.h"
+              "core/gimptempbuf.h"
+              "gimppdb-utils.h"
+              "gimppdbcontext.h"
+              "gimp-intl.h");
+
+@procs = qw(drawable_get_format
+            drawable_type
+            drawable_type_with_alpha
+            drawable_has_alpha
+            drawable_is_rgb
+            drawable_is_gray
+            drawable_is_indexed
+            drawable_bpp
+            drawable_width
+            drawable_height
+            drawable_offsets
+            drawable_set_image
+            drawable_mask_bounds
+            drawable_mask_intersect
+            drawable_merge_shadow
+            drawable_free_shadow
+            drawable_update
+            drawable_get_pixel drawable_set_pixel
+           drawable_fill
+            drawable_offset
+            drawable_thumbnail
+            drawable_sub_thumbnail
+            drawable_foreground_extract);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Drawable procedures';
+$doc_title = 'gimpdrawable';
+$doc_short_desc = 'Functions to manipulate drawables.';
+$doc_long_desc = 'Functions to manipulate drawables.';
+
+1;
diff --git a/pdb/groups/drawable_color.pdb b/pdb/groups/drawable_color.pdb
new file mode 100644
index 0000000..3d8a745
--- /dev/null
+++ b/pdb/groups/drawable_color.pdb
@@ -0,0 +1,838 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub drawable_brightness_contrast {
+    $blurb = 'Modify brightness/contrast in the specified drawable.';
+
+    $help = <<'HELP';
+This procedures allows the brightness and contrast of the specified drawable to
+be modified.  Both 'brightness' and 'contrast' parameters are defined between
+-0.5 and 0.5.
+HELP
+
+    &std_pdb_misc;
+    $date = '1997';
+    $since = '2.10';
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' },
+       { name => 'brightness', type => '-0.5 <= float <= 0.5',
+         desc => 'Brightness adjustment' },
+       { name => 'contrast', type => '-0.5 <= float <= 0.5',
+         desc => 'Contrast adjustment' }
+    );
+
+    %invoke = (
+        headers => [ qw("operations/gimpbrightnesscontrastconfig.h") ],
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GObject *config = g_object_new (GIMP_TYPE_BRIGHTNESS_CONTRAST_CONFIG,
+                                      "brightness", brightness,
+                                      "contrast",   contrast,
+                                      NULL);
+
+      gimp_drawable_apply_operation_by_name (drawable, progress,
+                                             C_("undo-type", "Brightness-Contrast"),
+                                             "gimp:brightness-contrast",
+                                             config);
+      g_object_unref (config);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub drawable_color_balance {
+    $blurb = 'Modify the color balance of the specified drawable.';
+
+    $help = <<'HELP';
+Modify the color balance of the specified drawable. There are three axis which
+can be modified: cyan-red, magenta-green, and yellow-blue. Negative values
+increase the amount of the former, positive values increase the amount of the
+latter. Color balance can be controlled with the 'transfer_mode' setting, which
+allows shadows, mid-tones, and highlights in an image to be affected
+differently. The 'preserve-lum' parameter, if TRUE, ensures that the
+luminosity of each pixel remains fixed.
+HELP
+
+    &std_pdb_misc;
+    $date = '1997';
+    $since = '2.10';
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' },
+       { name => 'transfer_mode', type => 'enum GimpTransferMode',
+         desc => 'Transfer mode' },
+       { name => 'preserve_lum', type => 'boolean',
+         desc => 'Preserve luminosity values at each pixel' },
+       { name => 'cyan_red', type => '-100 <= float <= 100',
+         desc => 'Cyan-Red color balance' },
+       { name => 'magenta_green', type => '-100 <= float <= 100',
+         desc => 'Magenta-Green color balance' },
+       { name => 'yellow_blue', type => '-100 <= float <= 100',
+         desc => 'Yellow-Blue color balance' }
+    );
+
+    %invoke = (
+       headers => [ qw("operations/gimpcolorbalanceconfig.h") ],
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error)  &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GObject *config = g_object_new (GIMP_TYPE_COLOR_BALANCE_CONFIG,
+                                      "range",               transfer_mode,
+                                      "preserve-luminosity", preserve_lum,
+                                      NULL);
+
+      g_object_set (config,
+                    "cyan-red",      cyan_red      / 100.0,
+                    "magenta-green", magenta_green / 100.0,
+                    "yellow-blue",   yellow_blue   / 100.0,
+                    NULL);
+
+      gimp_drawable_apply_operation_by_name (drawable, progress,
+                                             C_("undo-type", "Color Balance"),
+                                             "gimp:color-balance",
+                                             config);
+      g_object_unref (config);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub drawable_colorize_hsl {
+    $blurb = 'Render the drawable as a grayscale image seen through a colored glass.';
+
+    $help = <<'HELP';
+Desaturates the drawable, then tints it with the specified color. This tool is
+only valid on RGB color images. It will not operate on grayscale drawables.
+HELP
+
+    &neo_pdb_misc('2004', '2.10');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' },
+       { name => 'hue', type => '0 <= float <= 360',
+         desc => 'Hue in degrees' },
+       { name => 'saturation', type => '0 <= float <= 100',
+         desc => 'Saturation in percent' },
+       { name => 'lightness', type => '-100 <= float <= 100',
+         desc => 'Lightness in percent' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error) &&
+      ! gimp_drawable_is_gray (drawable))
+    {
+      GeglNode *node =
+        gegl_node_new_child (NULL,
+                             "operation", "gimp:colorize",
+                             "hue",        hue        / 360.0,
+                             "saturation", saturation / 100.0,
+                             "lightness",  lightness  / 100.0,
+                             NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Colorize"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub drawable_curves_explicit {
+    $blurb = 'Modifies the intensity curve(s) for specified drawable.';
+
+    $help = <<'HELP';
+Modifies the intensity mapping for one channel in the specified
+drawable. The channel can be either an intensity component, or the
+value. The 'values' parameter is an array of doubles which explicitly
+defines how each pixel value in the drawable will be modified.  Use
+the gimp_curves_spline() function to modify intensity levels with
+Catmull Rom splines.
+HELP
+
+    &std_pdb_misc;
+    $since = '2.10';
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' },
+       { name => 'channel', type => 'enum GimpHistogramChannel',
+         desc => 'The channel to modify' },
+       { name => 'values', type => 'floatarray',
+         desc => 'The explicit curve',
+         array => { name => 'num_values', type => '256 <= int32 <= 2096',
+                    desc => 'The number of values in the new curve' } }
+    );
+
+    %invoke = (
+       headers => [ qw("operations/gimpcurvesconfig.h") ],
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error) &&
+      (num_values >= 256) &&
+      (num_values <= 4096) &&
+      (gimp_drawable_has_alpha (drawable) || channel != GIMP_HISTOGRAM_ALPHA) &&
+      (! gimp_drawable_is_gray (drawable) ||
+       channel == GIMP_HISTOGRAM_VALUE || channel == GIMP_HISTOGRAM_ALPHA) &&
+      channel != GIMP_HISTOGRAM_LUMINANCE)
+    {
+      GObject *config = gimp_curves_config_new_explicit (channel,
+                                                         values,
+                                                         num_values);
+
+      gimp_drawable_apply_operation_by_name (drawable, progress,
+                                             C_("undo-type", "Curves"),
+                                             "gimp:curves",
+                                             config);
+      g_object_unref (config);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub drawable_curves_spline {
+    $blurb = 'Modifies the intensity curve(s) for specified drawable.';
+
+    $help = <<'HELP';
+Modifies the intensity mapping for one channel in the specified
+drawable. The channel can be either an intensity component, or the
+value. The 'points' parameter is an array of doubles which define a
+set of control points which describe a Catmull Rom spline which yields
+the final intensity curve. Use the gimp_curves_explicit() function to
+explicitly modify intensity levels.
+HELP
+
+    &std_pdb_misc;
+    $since = '2.10';
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' },
+       { name => 'channel', type => 'enum GimpHistogramChannel',
+         desc => 'The channel to modify' },
+       { name => 'points', type => 'floatarray',
+         desc => 'The spline control points: { cp1.x, cp1.y, cp2.x, cp2.y,
+                  ... }',
+         array => { name => 'num_points', type => '4 <= int32 <= 2048',
+                    desc => 'The number of values in the control point array' }
+       }
+    );
+
+    %invoke = (
+       headers => [ qw("operations/gimpcurvesconfig.h") ],
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error) &&
+      ! (num_points & 1) &&
+      (gimp_drawable_has_alpha (drawable) || channel != GIMP_HISTOGRAM_ALPHA) &&
+      (! gimp_drawable_is_gray (drawable) ||
+       channel == GIMP_HISTOGRAM_VALUE || channel == GIMP_HISTOGRAM_ALPHA) &&
+      channel != GIMP_HISTOGRAM_LUMINANCE)
+    {
+      GObject *config = gimp_curves_config_new_spline (channel,
+                                                       points,
+                                                       num_points / 2);
+
+      gimp_drawable_apply_operation_by_name (drawable, progress,
+                                             C_("undo-type", "Curves"),
+                                             "gimp:curves",
+                                             config);
+      g_object_unref (config);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub drawable_desaturate {
+    $blurb = <<'BLURB';
+Desaturate the contents of the specified drawable, with the specified formula.
+BLURB
+
+    $help = <<'HELP';
+This procedure desaturates the contents of the specified drawable,
+with the specified formula. This procedure only works on drawables of
+type RGB color.
+HELP
+
+    $author = $copyright = 'Karine Delvare';
+    $date = '2005';
+    $since = '2.10';
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' },
+       { name => 'desaturate_mode', type => 'enum GimpDesaturateMode',
+         desc => 'The formula to use to desaturate' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error) &&
+      gimp_drawable_is_rgb (drawable))
+    {
+      GeglNode *node =
+        gegl_node_new_child (NULL,
+                             "operation", "gimp:desaturate",
+                             "mode",      desaturate_mode,
+                             NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Desaturate"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub drawable_equalize {
+    $blurb = 'Equalize the contents of the specified drawable.';
+
+    $help = <<'HELP';
+This procedure equalizes the contents of the specified drawable. Each
+intensity channel is equalized independently. The equalized intensity
+is given as inten' = (255 - inten). The 'mask_only' option specifies
+whether to adjust only the area of the image within the selection
+bounds, or the entire image based on the histogram of the selected
+area. If there is no selection, the entire image is adjusted based on
+the histogram for the entire image.
+HELP
+
+    &std_pdb_misc;
+    $since = '2.10';
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' },
+       { name => 'mask_only', type => 'boolean',
+         desc => 'Equalization option' }
+    );
+
+    %invoke = (
+       headers => [ qw("core/gimpdrawable-equalize.h") ],
+       code => <<'CODE'
+{
+  if (! gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                   GIMP_PDB_ITEM_CONTENT, error) ||
+      ! gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    success = FALSE;
+
+  if (success)
+    gimp_drawable_equalize (drawable, mask_only);
+}
+CODE
+    );
+}
+
+sub drawable_histogram {
+    $blurb = <<'BLURB';
+Returns information on the intensity histogram for the specified drawable.
+BLURB
+
+    $help = <<'HELP';
+
+This tool makes it possible to gather information about the intensity
+histogram of a drawable. A channel to examine is first specified. This
+can be either value, red, green, or blue, depending on whether the
+drawable is of type color or grayscale. Second, a range of intensities
+are specified. The gimp_drawable_histogram() function returns
+statistics based on the pixels in the drawable that fall under this
+range of values. Mean, standard deviation, median, number of pixels,
+and percentile are all returned.  Additionally, the total count of
+pixels in the image is returned. Counts of pixels are weighted by any
+associated alpha values and by the current selection mask. That is,
+pixels that lie outside an active selection mask will not be
+counted. Similarly, pixels with transparent alpha values will not be
+counted. The returned mean, std_dev and median are in the range
+(0..255) for 8-bit images or if the plug-in is not precision-aware,
+and in the range (0.0..1.0) otherwise.
+
+HELP
+
+    &std_pdb_misc;
+    $since = '2.10';
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' },
+       { name => 'channel', type => 'enum GimpHistogramChannel',
+         desc => 'The channel to query' },
+       { name => 'start_range', type => '0.0 <= float <= 1.0',
+         desc => 'Start of the intensity measurement range' },
+       { name => 'end_range', type => '0.0 <= float <= 1.0',
+         desc => 'End of the intensity measurement range' }
+    );
+
+    @outargs = (
+       { name => 'mean', type => 'float', void_ret => 1,
+         desc => 'Mean intensity value' },
+       { name => 'std_dev',  type => 'float',
+         desc => 'Standard deviation of intensity values' },
+       { name => 'median',  type => 'float',
+         desc => 'Median intensity value' },
+       { name => 'pixels',  type => 'float',
+         desc => 'Alpha-weighted pixel count for entire image' },
+       { name => 'count',  type => 'float',
+         desc => 'Alpha-weighted pixel count for range' },
+       { name => 'percentile',  type => 'float',
+         desc => 'Percentile that range falls under' }
+    );
+
+    %invoke = (
+       headers => [ qw("core/gimpdrawable-histogram.h"
+                        "core/gimphistogram.h") ],
+       code => <<'CODE'
+{
+  if (! gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL, 0, error) ||
+      (! gimp_drawable_has_alpha (drawable) &&
+       channel == GIMP_HISTOGRAM_ALPHA) ||
+      (gimp_drawable_is_gray (drawable) &&
+       channel != GIMP_HISTOGRAM_VALUE && channel != GIMP_HISTOGRAM_ALPHA))
+    success = FALSE;
+
+  if (success)
+    {
+      GimpHistogram *histogram;
+      gint           n_bins;
+      gint           start;
+      gboolean       precision_enabled;
+      gboolean       linear;
+      gint           end;
+
+      precision_enabled =
+        gimp->plug_in_manager->current_plug_in &&
+        gimp_plug_in_precision_enabled (gimp->plug_in_manager->current_plug_in);
+
+      if (precision_enabled)
+        linear = gimp_drawable_get_linear (drawable);
+      else
+        linear = FALSE;
+
+      histogram = gimp_histogram_new (linear);
+      gimp_drawable_calculate_histogram (drawable, histogram, FALSE);
+
+      n_bins = gimp_histogram_n_bins (histogram);
+
+      start = ROUND (start_range * (n_bins - 1));
+      end   = ROUND (end_range   * (n_bins - 1));
+
+      mean       = gimp_histogram_get_mean (histogram, channel,
+                                           start, end);
+      std_dev    = gimp_histogram_get_std_dev (histogram, channel,
+                                              start, end);
+      median     = gimp_histogram_get_median (histogram, channel,
+                                             start, end);
+      pixels     = gimp_histogram_get_count (histogram, channel, 0, n_bins - 1);
+      count      = gimp_histogram_get_count (histogram, channel,
+                                             start, end);
+      percentile = count / pixels;
+
+      g_object_unref (histogram);
+
+      if (n_bins == 256 || ! precision_enabled)
+        {
+          mean    *= 255;
+          std_dev *= 255;
+          median  *= 255;
+        }
+    }
+}
+CODE
+    );
+}
+
+sub drawable_hue_saturation {
+    $blurb = <<'BLURB';
+Modify hue, lightness, and saturation in the specified drawable.
+BLURB
+
+    $help = <<'HELP';
+This procedure allows the hue, lightness, and saturation in the specified
+drawable to be modified. The 'hue-range' parameter provides the capability to
+limit range of affected hues. The 'overlap' parameter provides blending into
+neighboring hue channels when rendering.
+HELP
+
+    &std_pdb_misc;
+    $since = '2.10';
+
+    @inargs = (
+        { name => 'drawable', type => 'drawable',
+          desc => 'The drawable' },
+        { name => 'hue_range', type => 'enum GimpHueRange',
+          desc => 'Range of affected hues' },
+        { name => 'hue_offset', type => '-180 <= float <= 180',
+          desc => 'Hue offset in degrees' },
+        { name => 'lightness', type => '-100 <= float <= 100',
+          desc => 'Lightness modification' },
+        { name => 'saturation', type => '-100 <= float <= 100',
+          desc => 'Saturation modification' },
+        { name => 'overlap', type => '0 <= float <= 100',
+          desc => 'Overlap other hue channels' }
+    );
+
+    %invoke = (
+        headers => [ qw("operations/gimphuesaturationconfig.h") ],
+        code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GObject *config = g_object_new (GIMP_TYPE_HUE_SATURATION_CONFIG,
+                                      "range", hue_range,
+                                      NULL);
+
+       g_object_set (config,
+                     "hue",        hue_offset / 180.0,
+                     "saturation", saturation / 100.0,
+                     "lightness",  lightness  / 100.0,
+                     "overlap",    overlap / 100.0,
+                     NULL);
+
+      gimp_drawable_apply_operation_by_name (drawable, progress,
+                                             C_("undo-type", "Hue-Saturation"),
+                                             "gimp:hue-saturation",
+                                             config);
+      g_object_unref (config);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub drawable_invert {
+    $blurb = 'Invert the contents of the specified drawable.';
+
+    $help = <<'HELP';
+This procedure inverts the contents of the specified drawable. Each
+intensity channel is inverted independently. The inverted intensity is
+given as inten' = (255 - inten). If 'linear' is TRUE, the drawable is
+inverted in linear space.
+HELP
+
+    &std_pdb_misc;
+    $since = '2.10';
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' },
+       { name => 'linear', type => 'boolean',
+         desc => 'Whether to invert in linear space' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      gimp_drawable_apply_operation_by_name (drawable, progress,
+                                             C_("undo-type", "Invert"),
+                                             linear ?
+                                             "gegl:invert-linear" :
+                                             "gegl:invert-gamma",
+                                             NULL);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub drawable_levels {
+    $blurb = 'Modifies intensity levels in the specified drawable.';
+
+    $help = <<'HELP';
+This tool allows intensity levels in the specified drawable to be remapped
+according to a set of parameters. The low/high input levels specify an initial
+mapping from the source intensities. The gamma value determines how intensities
+between the low and high input intensities are interpolated. A gamma value of
+1.0 results in a linear interpolation. Higher gamma values result in more
+high-level intensities. Lower gamma values result in more low-level
+intensities. The low/high output levels constrain the final intensity
+mapping--that is, no final intensity will be lower than the low output level
+and no final intensity will be higher than the high output level. This tool is
+only valid on RGB color and grayscale images.
+HELP
+
+    &std_pdb_misc;
+    $since = '2.10';
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' },
+       { name => 'channel', type => 'enum GimpHistogramChannel',
+         desc => 'The channel to modify' },
+       { name => 'low_input', type => '0.0 <= float <= 1.0',
+         desc => "Intensity of lowest input" },
+       { name => 'high_input', type => '0.0 <= float <= 1.0',
+         desc => "Intensity of highest input" },
+       { name => 'gamma', type => '0.1 <= float <= 10',
+         desc => 'Gamma adjustment factor' },
+       { name => 'low_output', type => '0.0 <= float <= 1.0',
+         desc => "Intensity of lowest output" },
+       { name => 'high_output', type => '0.0 <= float <= 1.0',
+         desc => "Intensity of highest output" }
+    );
+
+    %invoke = (
+       headers => [ qw("operations/gimplevelsconfig.h") ],
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error) &&
+      (gimp_drawable_has_alpha (drawable) || channel != GIMP_HISTOGRAM_ALPHA) &&
+      (! gimp_drawable_is_gray (drawable) ||
+       channel == GIMP_HISTOGRAM_VALUE || channel == GIMP_HISTOGRAM_ALPHA) &&
+      channel != GIMP_HISTOGRAM_LUMINANCE)
+    {
+      GObject *config = g_object_new (GIMP_TYPE_LEVELS_CONFIG,
+                                      "channel", channel,
+                                      NULL);
+
+      g_object_set (config,
+                    "low-input",   low_input,
+                    "high-input",  high_input,
+                    "gamma",       gamma,
+                    "low-output",  low_output,
+                    "high-output", high_output,
+                    NULL);
+
+      gimp_drawable_apply_operation_by_name (drawable, progress,
+                                             C_("undo-type", "Levels"),
+                                             "gimp:levels",
+                                             config);
+      g_object_unref (config);
+    }
+  else
+    success = TRUE;
+}
+CODE
+    );
+}
+
+sub drawable_levels_stretch {
+    $blurb = 'Automatically modifies intensity levels in the specified drawable.';
+
+    $help = <<'HELP';
+This procedure allows intensity levels in the specified drawable to be
+remapped according to a set of guessed parameters. It is equivalent to
+clicking the "Auto" button in the Levels tool.
+HELP
+
+    $author = $copyright = 'Joao S.O. Bueno, Shawn Willden';
+    $date = '2003';
+    $since = '2.10';
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' }
+    );
+
+    %invoke = (
+        headers => [ qw("core/gimpdrawable-levels.h") ],
+        code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      gimp_drawable_levels_stretch (drawable, progress);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub drawable_posterize {
+    $blurb = 'Posterize the specified drawable.';
+
+    $help = <<'HELP';
+This procedures reduces the number of shades allows in each intensity channel
+to the specified 'levels' parameter.
+HELP
+
+    &std_pdb_misc;
+    $date = '1997';
+    $since = '2.10';
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' },
+       { name => 'levels', type => '2 <= int32 <= 255',
+         desc => 'Levels of posterization' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node =
+        gegl_node_new_child (NULL,
+                             "operation", "gimp:posterize",
+                             "levels",    levels,
+                             NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Posterize"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub drawable_threshold {
+    $blurb = 'Threshold the specified drawable.';
+
+    $help = <<'HELP';
+This procedures generates a threshold map of the specified
+drawable. All pixels between the values of 'low_threshold' and
+'high_threshold', on the scale of 'channel' are replaced with white,
+and all other pixels with black.
+HELP
+
+    &std_pdb_misc;
+    $date = '1997';
+    $since = '2.10';
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable' },
+       { name => 'channel', type => 'enum GimpHistogramChannel',
+         desc => 'The channel to base the threshold on' },
+       { name => 'low_threshold', type => '0.0 <= float <= 1.0',
+         desc => 'The low threshold value' },
+       { name => 'high_threshold', type => '0.0 <= float <= 1.0',
+         desc => 'The high threshold value' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node =
+        gegl_node_new_child (NULL,
+                             "operation", "gimp:threshold",
+                             "channel",   channel,
+                             "low",       low_threshold,
+                             "high",      high_threshold,
+                             NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Threshold"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+@headers = qw("libgimpmath/gimpmath.h"
+              "core/gimp.h"
+              "core/gimpdrawable.h"
+              "core/gimpdrawable-operation.h"
+              "plug-in/gimpplugin.h"
+              "plug-in/gimppluginmanager.h"
+              "gimppdb-utils.h"
+              "gimp-intl.h");
+
+@procs = qw(drawable_brightness_contrast
+            drawable_color_balance
+            drawable_colorize_hsl
+            drawable_curves_explicit
+            drawable_curves_spline
+            drawable_desaturate
+            drawable_equalize
+            drawable_histogram
+            drawable_hue_saturation
+            drawable_invert
+            drawable_levels
+            drawable_levels_stretch
+            drawable_posterize
+            drawable_threshold);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Color';
+$doc_title = 'gimpdrawablecolor';
+$doc_short_desc = "Functions for manipulating a drawable's color.";
+$doc_long_desc = "Functions for manipulating a drawable's color, including curves and histograms.";
+
+1;
diff --git a/pdb/groups/drawable_transform.pdb b/pdb/groups/drawable_transform.pdb
new file mode 100644
index 0000000..4b36edf
--- /dev/null
+++ b/pdb/groups/drawable_transform.pdb
@@ -0,0 +1,934 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+# Derived  from old transform_tools by
+# Joao S. O. Bueno Calligaris <gwidion mpc com br>
+
+# Drawable Transformations
+
+# shortcuts
+
+sub transform_invoke {
+    my ($progress_text, $assemble_matrix, $check) = @_;
+    my $success_check = 'gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                                    GIMP_PDB_ITEM_CONTENT |
+                                                    GIMP_PDB_ITEM_POSITION,
+                                                    error);';
+
+    if ($check) {
+        $success_check = "(gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                                      GIMP_PDB_ITEM_CONTENT |
+                                                      GIMP_PDB_ITEM_POSITION, error) && " . $check . ");";
+    }
+
+    %invoke = (
+        code => <<"CODE"
+{
+  gint x, y, width, height;
+
+  success = $success_check
+
+  if (success &&
+      gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
+    {
+      GimpMatrix3 matrix;
+      gint        off_x, off_y;
+
+      gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y);
+
+      x += off_x;
+      y += off_y;
+
+      /* Assemble the transformation matrix */
+$assemble_matrix
+
+      if (progress)
+        gimp_progress_start (progress, FALSE, _(\"$progress_text\"));
+
+      if (! gimp_viewable_get_children (GIMP_VIEWABLE (drawable)) &&
+          ! gimp_channel_is_empty (gimp_image_get_mask (gimp_item_get_image (GIMP_ITEM (drawable)))))
+        {
+          if (! gimp_drawable_transform_affine (drawable, context,
+                                                &matrix, transform_direction,
+                                                interpolation,
+                                                clip_result, progress))
+            {
+              success = FALSE;
+            }
+        }
+      else
+        {
+          gimp_item_transform (GIMP_ITEM (drawable), context, &matrix,
+                               transform_direction,
+                               interpolation,
+                               clip_result, progress);
+        }
+
+      if (progress)
+        gimp_progress_end (progress);
+    }
+}
+CODE
+    )
+}
+
+sub transform_default_invoke {
+    my ($progress_text, $assemble_matrix, $check) = @_;
+    my $success_check = 'gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                                    GIMP_PDB_ITEM_CONTENT |
+                                                    GIMP_PDB_ITEM_POSITION, error);';
+
+    if ($check) {
+        $success_check = "(gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                                      GIMP_PDB_ITEM_CONTENT |
+                                                      GIMP_PDB_ITEM_POSITION, error) && " . $check . ");";
+    }
+
+    %invoke = (
+        code => <<"CODE"
+{
+  gint x, y, width, height;
+
+  success = $success_check
+
+  if (success &&
+      gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
+    {
+      GimpMatrix3           matrix;
+      GimpInterpolationType interpolation_type = GIMP_INTERPOLATION_NONE;
+      gint                  off_x, off_y;
+
+      gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y);
+
+      x += off_x;
+      y += off_y;
+
+      /* Assemble the transformation matrix */
+$assemble_matrix
+
+      if (interpolate)
+        interpolation_type = gimp->config->interpolation_type;
+
+      if (progress)
+        gimp_progress_start (progress, FALSE, _(\"$progress_text\"));
+
+      if (! gimp_viewable_get_children (GIMP_VIEWABLE (drawable)) &&
+          ! gimp_channel_is_empty (gimp_image_get_mask (gimp_item_get_image (GIMP_ITEM (drawable)))))
+        {
+          if (! gimp_drawable_transform_affine (drawable, context,
+                                                &matrix, GIMP_TRANSFORM_FORWARD,
+                                                interpolation_type,
+                                                clip_result, progress))
+            {
+              success = FALSE;
+            }
+        }
+      else
+        {
+          gimp_item_transform (GIMP_ITEM (drawable), context, &matrix,
+                               GIMP_TRANSFORM_FORWARD,
+                               interpolation_type,
+                               clip_result, progress);
+        }
+    
+      if (progress)
+        gimp_progress_end (progress);
+    }
+}
+CODE
+    )
+}
+
+
+# The defs
+
+sub drawable_transform_flip_simple {
+    &std_pdb_deprecated('gimp-item-transform-flip-simple');
+    &joao_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+        { name => 'flip_type',
+         type => 'enum GimpOrientationType (no GIMP_ORIENTATION_UNKNOWN)',
+          desc => 'Type of flip' },
+        { name => 'auto_center', type => 'boolean',
+          desc => 'Whether to automatically position the axis in the selection center' },
+        { name => 'axis', type => 'float',
+          desc => 'coord. of flip axis' },
+        { name => 'clip_result', type => 'boolean',
+          desc => 'Whether to clip results' }
+    );
+
+    @outargs = (
+       { name => 'drawable', type => 'drawable', no_declare => 1,
+         desc => 'The flipped drawable' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  gint x, y, width, height;
+
+  success = gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                       GIMP_PDB_ITEM_CONTENT |
+                                       GIMP_PDB_ITEM_POSITION, error);
+
+  if (success &&
+      gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
+    {
+      gimp_transform_get_flip_axis (x, y, width, height,
+                                    flip_type, auto_center, &axis);
+
+      if (! gimp_viewable_get_children (GIMP_VIEWABLE (drawable)) &&
+          ! gimp_channel_is_empty (gimp_image_get_mask (gimp_item_get_image (GIMP_ITEM (drawable)))))
+        {
+          if (! gimp_drawable_transform_flip (drawable, context,
+                                              flip_type, axis, clip_result))
+            {
+              success = FALSE;
+            }
+        }
+      else
+        {
+          gimp_item_flip (GIMP_ITEM (drawable), context,
+                          flip_type, axis, clip_result);
+        }
+    }
+}
+CODE
+    );
+}
+
+
+sub drawable_transform_flip {
+    &std_pdb_deprecated('gimp-item-transform-flip');
+    &joao_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+        { name => 'x0', type => 'float',
+          desc => 'horz. coord. of one end of axis' },
+        { name => 'y0', type => 'float',
+          desc => 'vert. coord. of one end of axis' },
+        { name => 'x1', type => 'float',
+          desc => 'horz. coord. of other end of axis' },
+        { name => 'y1', type => 'float',
+          desc => 'vert. coord. of other end of axis' },
+       { name => 'transform_direction', type => 'enum GimpTransformDirection',
+         desc => 'Direction of transformation' },
+       { name => 'interpolation', type => 'enum GimpInterpolationType',
+         desc => 'Type of interpolation' },
+       { name => 'supersample', type => 'boolean', dead => 1,
+         desc => 'This parameter is ignored' },
+       { name => 'recursion_level', type => '1 <= int32', dead => 1,
+         desc => 'This parameter is ignored' },
+       { name => 'clip_result', type => 'boolean',
+         desc => 'Whether to clip results' }
+    );
+
+    @outargs = (
+       { name => 'drawable', type => 'drawable', no_declare => 1,
+         desc => 'The flipped drawable' }
+    );
+
+    transform_invoke ("Flipping", <<CODE);
+      gimp_matrix3_identity (&matrix);
+      gimp_transform_matrix_flip_free (&matrix,
+                                       x0, y0, x1, y1);
+CODE
+}
+
+
+sub drawable_transform_flip_default {
+    &std_pdb_deprecated('gimp-item-transform-flip');
+    &joao_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+        { name => 'x0', type => 'float',
+          desc => 'horz. coord. of one end of axis' },
+        { name => 'y0', type => 'float',
+          desc => 'vert. coord. of one end of axis' },
+        { name => 'x1', type => 'float',
+          desc => 'horz. coord. of other end of axis' },
+        { name => 'y1', type => 'float',
+          desc => 'vert. coord. of other end of axis' },
+       { name => 'interpolate', type => 'boolean',
+         desc => 'Whether to use interpolation and supersampling' },
+       { name => 'clip_result', type => 'boolean',
+         desc => 'Whether to clip results' }
+    );
+
+    @outargs = (
+       { name => 'drawable', type => 'drawable', no_declare => 1,
+         desc => 'The flipped drawable' }
+    );
+
+    transform_default_invoke ("Flipping", <<CODE);
+      gimp_matrix3_identity (&matrix);
+      gimp_transform_matrix_flip_free (&matrix,
+                                       x0, y0, x1, y1);
+CODE
+}
+
+
+sub drawable_transform_perspective {
+    &std_pdb_deprecated('gimp-item-transform-perspective');
+    &joao_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+       { name => 'x0', type => 'float',
+         desc => 'The new x coordinate of upper-left corner of original
+                  bounding box' },
+       { name => 'y0', type => 'float',
+         desc => 'The new y coordinate of upper-left corner of original
+                  bounding box' },
+       { name => 'x1', type => 'float',
+         desc => 'The new x coordinate of upper-right corner of original
+                  bounding box' },
+       { name => 'y1', type => 'float',
+         desc => 'The new y coordinate of upper-right corner of original
+                  bounding box' },
+       { name => 'x2', type => 'float',
+         desc => 'The new x coordinate of lower-left corner of original
+                  bounding box' },
+       { name => 'y2', type => 'float',
+         desc => 'The new y coordinate of lower-left corner of original
+                  bounding box' },
+       { name => 'x3', type => 'float',
+         desc => 'The new x coordinate of lower-right corner of original
+                  bounding box' },
+       { name => 'y3', type => 'float',
+         desc => 'The new y coordinate of lower-right corner of original
+                  bounding box' },
+       { name => 'transform_direction', type => 'enum GimpTransformDirection',
+         desc => 'Direction of transformation' },
+       { name => 'interpolation', type => 'enum GimpInterpolationType',
+         desc => 'Type of interpolation' },
+       { name => 'supersample', type => 'boolean', dead => 1,
+         desc => 'This parameter is ignored' },
+       { name => 'recursion_level', type => '1 <= int32', dead => 1,
+         desc => 'This parameter is ignored' },
+       { name => 'clip_result', type => 'enum GimpTransformResize',
+         desc => 'How to clip results' }
+    );
+
+    @outargs = (
+       { name => 'drawable', type => 'drawable', no_declare => 1,
+         desc => 'The newly mapped drawable' }
+    );
+
+    transform_invoke ("Perspective", <<CODE);
+      gimp_matrix3_identity (&matrix);
+      gimp_transform_matrix_perspective (&matrix,
+                                         x, y, width, height,
+                                         x0, y0, x1, y1,
+                                         x2, y2, x3, y3);
+CODE
+}
+
+
+sub drawable_transform_perspective_default {
+    &std_pdb_deprecated('gimp-item-transform-perspective');
+    &joao_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+       { name => 'x0', type => 'float',
+         desc => 'The new x coordinate of upper-left corner of original
+                  bounding box' },
+       { name => 'y0', type => 'float',
+         desc => 'The new y coordinate of upper-left corner of original
+                  bounding box' },
+       { name => 'x1', type => 'float',
+         desc => 'The new x coordinate of upper-right corner of original
+                  bounding box' },
+       { name => 'y1', type => 'float',
+         desc => 'The new y coordinate of upper-right corner of original
+                  bounding box' },
+       { name => 'x2', type => 'float',
+         desc => 'The new x coordinate of lower-left corner of original
+                  bounding box' },
+       { name => 'y2', type => 'float',
+         desc => 'The new y coordinate of lower-left corner of original
+                  bounding box' },
+       { name => 'x3', type => 'float',
+         desc => 'The new x coordinate of lower-right corner of original
+                  bounding box' },
+       { name => 'y3', type => 'float',
+         desc => 'The new y coordinate of lower-right corner of original
+                  bounding box' },
+       { name => 'interpolate', type => 'boolean',
+         desc => 'Whether to use interpolation and supersampling' },
+       { name => 'clip_result', type => 'enum GimpTransformResize',
+         desc => 'How to clip results' }
+    );
+
+    @outargs = (
+       { name => 'drawable', type => 'drawable', no_declare => 1,
+         desc => 'The newly mapped drawable' }
+    );
+
+    transform_default_invoke ("Perspective", <<CODE);
+      gimp_matrix3_identity (&matrix);
+      gimp_transform_matrix_perspective (&matrix,
+                                         x, y, width, height,
+                                         x0, y0, x1, y1,
+                                         x2, y2, x3, y3);
+CODE
+}
+
+
+sub drawable_transform_rotate_simple {
+    &std_pdb_deprecated('gimp-item-transform-rotate-simple');
+    &joao_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+        { name => 'rotate_type', type => 'enum GimpRotationType',
+          desc => 'Type of rotation' },
+        { name => 'auto_center', type => 'boolean',
+          desc => 'Whether to automatically rotate around the selection center' },
+        { name => 'center_x', type => 'int32',
+          desc => 'The hor. coordinate of the center of rotation' },
+        { name => 'center_y', type => 'int32',
+          desc => 'The vert. coordinate of the center of rotation' },
+        { name => 'clip_result', type => 'boolean',
+          desc => 'Whether to clip results' }
+    );
+
+    @outargs = (
+       { name => 'drawable', type => 'drawable', no_declare => 1,
+         desc => 'The rotated drawable' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  gint x, y, width, height;
+
+  success = gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                       GIMP_PDB_ITEM_CONTENT |
+                                       GIMP_PDB_ITEM_POSITION, error);
+
+  if (success &&
+      gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
+    {
+      gdouble cx = center_x;
+      gdouble cy = center_y;
+
+      gimp_transform_get_rotate_center (x, y, width, height,
+                                        auto_center, &cx, &cy);
+
+      if (! gimp_viewable_get_children (GIMP_VIEWABLE (drawable)) &&
+          ! gimp_channel_is_empty (gimp_image_get_mask (gimp_item_get_image (GIMP_ITEM (drawable)))))
+        {
+          if (! gimp_drawable_transform_rotate (drawable, context,
+                                                rotate_type, cx, cy,
+                                                clip_result))
+            {
+              success = FALSE;
+            }
+        }
+      else
+        {
+          gimp_item_rotate (GIMP_ITEM (drawable), context,
+                            rotate_type, cx, cy,
+                            clip_result);
+        }
+    }
+}
+CODE
+    );
+}
+
+sub drawable_transform_rotate {
+    &std_pdb_deprecated('gimp-item-transform-rotate');
+    &joao_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+        { name => 'angle', type => 'float',
+          desc => 'The angle of rotation (radians)' },
+        { name => 'auto_center', type => 'boolean',
+          desc => 'Whether to automatically rotate around the selection center' },
+        { name => 'center_x', type => 'int32',
+          desc => 'The hor. coordinate of the center of rotation' },
+        { name => 'center_y', type => 'int32',
+          desc => 'The vert. coordinate of the center of rotation' },
+       { name => 'transform_direction', type => 'enum GimpTransformDirection',
+         desc => 'Direction of transformation' },
+       { name => 'interpolation', type => 'enum GimpInterpolationType',
+         desc => 'Type of interpolation' },
+       { name => 'supersample', type => 'boolean', dead => 1,
+         desc => 'This parameter is ignored' },
+       { name => 'recursion_level', type => '1 <= int32', dead => 1,
+         desc => 'This parameter is ignored' },
+       { name => 'clip_result', type => 'enum GimpTransformResize',
+         desc => 'How to clip results' }
+    );
+
+    @outargs = (
+       { name => 'drawable', type => 'drawable', no_declare => 1,
+         desc => 'The rotated drawable' }
+    );
+
+    transform_invoke ("Rotating", <<CODE);
+      gimp_matrix3_identity (&matrix);
+      if (auto_center)
+        gimp_transform_matrix_rotate_rect (&matrix,
+                                           x, y, width, height, angle);
+      else
+        gimp_transform_matrix_rotate_center (&matrix,
+                                             center_x, center_y, angle);
+CODE
+}
+
+
+sub drawable_transform_rotate_default {
+    &std_pdb_deprecated('gimp-item-transform-rotate');
+    &joao_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+        { name => 'angle', type => 'float',
+          desc => 'The angle of rotation (radians)' },
+        { name => 'auto_center', type => 'boolean',
+          desc => 'Whether to automatically rotate around the selection center' },
+        { name => 'center_x', type => 'int32',
+          desc => 'The hor. coordinate of the center of rotation' },
+        { name => 'center_y', type => 'int32',
+          desc => 'The vert. coordinate of the center of rotation' },
+       { name => 'interpolate', type => 'boolean',
+         desc => 'Whether to use interpolation and supersampling' },
+       { name => 'clip_result', type => 'enum GimpTransformResize',
+         desc => 'How to clip results' }
+    );
+
+    @outargs = (
+       { name => 'drawable', type => 'drawable', no_declare => 1,
+         desc => 'The rotated drawable' }
+    );
+
+    transform_default_invoke ("Rotating", <<CODE);
+      gimp_matrix3_identity (&matrix);
+      if (auto_center)
+        gimp_transform_matrix_rotate_rect (&matrix,
+                                           x, y, width, height, angle);
+      else
+        gimp_transform_matrix_rotate_center (&matrix,
+                                             center_x, center_y, angle);
+CODE
+}
+
+
+sub drawable_transform_scale {
+    &std_pdb_deprecated('gimp-item-transform-scale');
+    &joao_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+       { name => 'x0', type => 'float',
+         desc => 'The new x coordinate of the upper-left corner of the
+                   scaled region' },
+       { name => 'y0', type => 'float',
+         desc => 'The new y coordinate of the upper-left corner of the
+                   scaled region' },
+       { name => 'x1', type => 'float',
+         desc => 'The new x coordinate of the lower-right corner of the
+                   scaled region' },
+       { name => 'y1', type => 'float',
+         desc => 'The new y coordinate of the lower-right corner of the
+                   scaled region' },
+       { name => 'transform_direction', type => 'enum GimpTransformDirection',
+         desc => 'Direction of transformation' },
+       { name => 'interpolation', type => 'enum GimpInterpolationType',
+         desc => 'Type of interpolation' },
+       { name => 'supersample', type => 'boolean', dead => 1,
+         desc => 'This parameter is ignored' },
+       { name => 'recursion_level', type => '1 <= int32', dead => 1,
+         desc => 'This parameter is ignored' },
+       { name => 'clip_result', type => 'enum GimpTransformResize',
+         desc => 'How to clip results' }
+    );
+
+    @outargs = (
+       { name => 'drawable', type => 'drawable', no_declare => 1,
+         desc => 'The scaled drawable' }
+    );
+
+    transform_invoke ("Scaling", <<CODE, 'x0 < x1 && y0 < y1');
+      gimp_matrix3_identity (&matrix);
+      gimp_transform_matrix_scale (&matrix,
+                                   x, y, width, height,
+                                   x0, y0, x1 - x0, y1 - y0);
+CODE
+}
+
+
+sub drawable_transform_scale_default {
+    &std_pdb_deprecated('gimp-item-transform-scale');
+    &joao_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+       { name => 'x0', type => 'float',
+         desc => 'The new x coordinate of the upper-left corner of the
+                   scaled region' },
+       { name => 'y0', type => 'float',
+         desc => 'The new y coordinate of the upper-left corner of the
+                   scaled region' },
+       { name => 'x1', type => 'float',
+         desc => 'The new x coordinate of the lower-right corner of the
+                   scaled region' },
+       { name => 'y1', type => 'float',
+         desc => 'The new y coordinate of the lower-right corner of the
+                   scaled region' },
+       { name => 'interpolate', type => 'boolean',
+         desc => 'Whether to use interpolation and supersampling' },
+       { name => 'clip_result', type => 'enum GimpTransformResize',
+         desc => 'How to clip results' }
+     );
+
+    @outargs = (
+       { name => 'drawable', type => 'drawable', no_declare => 1,
+         desc => 'The scaled drawable' }
+    );
+
+    transform_default_invoke ("Scaling", <<CODE, 'x0 < x1 && y0 < y1');
+      gimp_matrix3_identity (&matrix);
+      gimp_transform_matrix_scale (&matrix,
+                                   x, y, width, height,
+                                   x0, y0, x1 - x0, y1 - y0);
+CODE
+}
+
+
+sub drawable_transform_shear {
+    &std_pdb_deprecated('gimp-item-transform-shear');
+    &joao_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+        { name => 'shear_type',
+         type => 'enum GimpOrientationType (no GIMP_ORIENTATION_UNKNOWN)',
+          desc => 'Type of shear' },
+        { name => 'magnitude', type => 'float',
+          desc => 'The magnitude of the shear' },
+       { name => 'transform_direction', type => 'enum GimpTransformDirection',
+         desc => 'Direction of transformation' },
+       { name => 'interpolation', type => 'enum GimpInterpolationType',
+         desc => 'Type of interpolation' },
+       { name => 'supersample', type => 'boolean', dead => 1,
+         desc => 'This parameter is ignored' },
+       { name => 'recursion_level', type => '1 <= int32', dead => 1,
+         desc => 'This parameter is ignored' },
+       { name => 'clip_result', type => 'enum GimpTransformResize',
+         desc => 'How to clip results' }
+    );
+
+    @outargs = (
+       { name => 'drawable', type => 'drawable', no_declare => 1,
+         desc => 'The sheared drawable' }
+    );
+
+    transform_invoke ("Shearing", <<CODE);
+      gimp_matrix3_identity (&matrix);
+      gimp_transform_matrix_shear (&matrix,
+                                   x, y, width, height,
+                                   shear_type, magnitude);
+CODE
+}
+
+
+sub drawable_transform_shear_default {
+    &std_pdb_deprecated('gimp-item-transform-shear');
+    &joao_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+        { name => 'shear_type',
+         type => 'enum GimpOrientationType (no GIMP_ORIENTATION_UNKNOWN)',
+          desc => 'Type of shear' },
+        { name => 'magnitude', type => 'float',
+          desc => 'The magnitude of the shear' },
+       { name => 'interpolate', type => 'boolean',
+         desc => 'Whether to use interpolation and supersampling' },
+       { name => 'clip_result', type => 'enum GimpTransformResize',
+         desc => 'How to clip results' }
+    );
+
+    @outargs = (
+       { name => 'drawable', type => 'drawable', no_declare => 1,
+         desc => 'The sheared drawable' }
+    );
+
+    transform_default_invoke ("Shearing", <<CODE);
+      gimp_matrix3_identity (&matrix);
+      gimp_transform_matrix_shear (&matrix,
+                                   x, y, width, height,
+                                   shear_type, magnitude);
+CODE
+}
+
+
+sub drawable_transform_2d {
+    &std_pdb_deprecated('gimp-item-transform-2d');
+    &joao_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+        { name => 'source_x', type => 'float',
+          desc => 'X coordinate of the transformation center' },
+        { name => 'source_y', type => 'float',
+          desc => 'Y coordinate of the transformation center' },
+        { name => 'scale_x', type => 'float',
+          desc => 'Amount to scale in x direction' },
+        { name => 'scale_y', type => 'float',
+          desc => 'Amount to scale in y direction' },
+        { name => 'angle', type => 'float',
+          desc => 'The angle of rotation (radians)' },
+        { name => 'dest_x', type => 'float',
+          desc => 'X coordinate of where the center goes' },
+        { name => 'dest_y', type => 'float',
+          desc => 'Y coordinate of where the center goes' },
+       { name => 'transform_direction', type => 'enum GimpTransformDirection',
+         desc => 'Direction of transformation' },
+       { name => 'interpolation', type => 'enum GimpInterpolationType',
+         desc => 'Type of interpolation' },
+       { name => 'supersample', type => 'boolean', dead => 1,
+         desc => 'This parameter is ignored' },
+       { name => 'recursion_level', type => '1 <= int32', dead => 1,
+         desc => 'This parameter is ignored' },
+       { name => 'clip_result', type => 'enum GimpTransformResize',
+         desc => 'How to clip results' }
+    );
+
+    @outargs = (
+       { name => 'drawable', type => 'drawable', no_declare => 1,
+         desc => 'The transformed drawable' }
+    );
+
+    transform_invoke ("2D Transform", <<CODE);
+      gimp_matrix3_identity  (&matrix);
+      gimp_matrix3_translate (&matrix, -source_x, -source_y);
+      gimp_matrix3_scale     (&matrix, scale_x, scale_y);
+      gimp_matrix3_rotate    (&matrix, angle);
+      gimp_matrix3_translate (&matrix, dest_x, dest_y);
+CODE
+}
+
+
+sub drawable_transform_2d_default {
+    &std_pdb_deprecated('gimp-item-transform-2d');
+    &joao_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+        { name => 'source_x', type => 'float',
+          desc => 'X coordinate of the transformation center' },
+        { name => 'source_y', type => 'float',
+          desc => 'Y coordinate of the transformation center' },
+        { name => 'scale_x', type => 'float',
+          desc => 'Amount to scale in x direction' },
+        { name => 'scale_y', type => 'float',
+          desc => 'Amount to scale in y direction' },
+        { name => 'angle', type => 'float',
+          desc => 'The angle of rotation (radians)' },
+        { name => 'dest_x', type => 'float',
+          desc => 'X coordinate of where the center goes' },
+        { name => 'dest_y', type => 'float',
+          desc => 'Y coordinate of where the center goes' },
+       { name => 'interpolate', type => 'boolean',
+         desc => 'Whether to use interpolation and supersampling' },
+       { name => 'clip_result', type => 'enum GimpTransformResize',
+         desc => 'How to clip results' }
+    );
+
+    @outargs = (
+       { name => 'drawable', type => 'drawable', no_declare => 1,
+         desc => 'The transformed drawable' }
+    );
+
+    transform_default_invoke ("2D Transforming", <<CODE);
+      gimp_matrix3_identity  (&matrix);
+      gimp_matrix3_translate (&matrix, -source_x, -source_y);
+      gimp_matrix3_scale     (&matrix, scale_x, scale_y);
+      gimp_matrix3_rotate    (&matrix, angle);
+      gimp_matrix3_translate (&matrix, dest_x, dest_y);
+CODE
+}
+
+
+sub drawable_transform_matrix {
+    &std_pdb_deprecated('gimp-item-transform-matrix');
+    &joao_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+        { name => 'coeff_0_0', type => 'float',
+          desc => 'coefficient (0,0) of the transformation matrix' },
+        { name => 'coeff_0_1', type => 'float',
+          desc => 'coefficient (0,1) of the transformation matrix' },
+        { name => 'coeff_0_2', type => 'float',
+          desc => 'coefficient (0,2) of the transformation matrix' },
+        { name => 'coeff_1_0', type => 'float',
+          desc => 'coefficient (1,0) of the transformation matrix' },
+        { name => 'coeff_1_1', type => 'float',
+          desc => 'coefficient (1,1) of the transformation matrix' },
+        { name => 'coeff_1_2', type => 'float',
+          desc => 'coefficient (1,2) of the transformation matrix' },
+        { name => 'coeff_2_0', type => 'float',
+          desc => 'coefficient (2,0) of the transformation matrix' },
+        { name => 'coeff_2_1', type => 'float',
+          desc => 'coefficient (2,1) of the transformation matrix' },
+        { name => 'coeff_2_2', type => 'float',
+          desc => 'coefficient (2,2) of the transformation matrix' },
+       { name => 'transform_direction', type => 'enum GimpTransformDirection',
+         desc => 'Direction of transformation' },
+       { name => 'interpolation', type => 'enum GimpInterpolationType',
+         desc => 'Type of interpolation' },
+       { name => 'supersample', type => 'boolean', dead => 1,
+         desc => 'This parameter is ignored' },
+       { name => 'recursion_level', type => '1 <= int32', dead => 1,
+         desc => 'This parameter is ignored' },
+       { name => 'clip_result', type => 'enum GimpTransformResize',
+         desc => 'How to clip results' }
+    );
+
+    @outargs = (
+       { name => 'drawable', type => 'drawable', no_declare => 1,
+         desc => 'The transformed drawable' }
+    );
+
+    transform_invoke ("2D Transforming", <<CODE);
+      matrix.coeff[0][0] = coeff_0_0;
+      matrix.coeff[0][1] = coeff_0_1;
+      matrix.coeff[0][2] = coeff_0_2;
+      matrix.coeff[1][0] = coeff_1_0;
+      matrix.coeff[1][1] = coeff_1_1;
+      matrix.coeff[1][2] = coeff_1_2;
+      matrix.coeff[2][0] = coeff_2_0;
+      matrix.coeff[2][1] = coeff_2_1;
+      matrix.coeff[2][2] = coeff_2_2;
+CODE
+}
+
+
+sub drawable_transform_matrix_default {
+    &std_pdb_deprecated('gimp-item-transform-matrix');
+    &joao_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+        { name => 'coeff_0_0', type => 'float',
+          desc => 'coefficient (0,0) of the transformation matrix' },
+        { name => 'coeff_0_1', type => 'float',
+          desc => 'coefficient (0,1) of the transformation matrix' },
+        { name => 'coeff_0_2', type => 'float',
+          desc => 'coefficient (0,2) of the transformation matrix' },
+        { name => 'coeff_1_0', type => 'float',
+          desc => 'coefficient (1,0) of the transformation matrix' },
+        { name => 'coeff_1_1', type => 'float',
+          desc => 'coefficient (1,1) of the transformation matrix' },
+        { name => 'coeff_1_2', type => 'float',
+          desc => 'coefficient (1,2) of the transformation matrix' },
+        { name => 'coeff_2_0', type => 'float',
+          desc => 'coefficient (2,0) of the transformation matrix' },
+        { name => 'coeff_2_1', type => 'float',
+          desc => 'coefficient (2,1) of the transformation matrix' },
+        { name => 'coeff_2_2', type => 'float',
+          desc => 'coefficient (2,2) of the transformation matrix' },
+       { name => 'interpolate', type => 'boolean',
+         desc => 'Whether to use interpolation and supersampling' },
+       { name => 'clip_result', type => 'enum GimpTransformResize',
+         desc => 'How to clip results' }
+    );
+
+    @outargs = (
+       { name => 'drawable', type => 'drawable', no_declare => 1,
+         desc => 'The transformed drawable' }
+    );
+
+    transform_default_invoke ("2D Transforming", <<CODE);
+      matrix.coeff[0][0] = coeff_0_0;
+      matrix.coeff[0][1] = coeff_0_1;
+      matrix.coeff[0][2] = coeff_0_2;
+      matrix.coeff[1][0] = coeff_1_0;
+      matrix.coeff[1][1] = coeff_1_1;
+      matrix.coeff[1][2] = coeff_1_2;
+      matrix.coeff[2][0] = coeff_2_0;
+      matrix.coeff[2][1] = coeff_2_1;
+      matrix.coeff[2][2] = coeff_2_2;
+CODE
+}
+
+
+@headers = qw("libgimpmath/gimpmath.h"
+              "config/gimpcoreconfig.h"
+              "core/gimp.h"
+              "core/gimp-transform-utils.h"
+              "core/gimpchannel.h"
+              "core/gimpimage.h"
+              "core/gimpdrawable.h"
+              "core/gimpdrawable-transform.h"
+              "core/gimpprogress.h"
+              "gimppdb-utils.h"
+              "gimp-intl.h");
+
+@procs = qw(drawable_transform_flip_simple
+            drawable_transform_flip
+            drawable_transform_flip_default
+            drawable_transform_perspective
+            drawable_transform_perspective_default
+            drawable_transform_rotate_simple
+            drawable_transform_rotate
+            drawable_transform_rotate_default
+            drawable_transform_scale
+            drawable_transform_scale_default
+            drawable_transform_shear
+            drawable_transform_shear_default
+            drawable_transform_2d
+            drawable_transform_2d_default
+            drawable_transform_matrix
+            drawable_transform_matrix_default);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Transformation procedures';
+$doc_title = 'gimpdrawabletransform';
+$doc_short_desc = 'Functions to perform transformatrions on drawables.';
+$doc_long_desc = 'Functions to perform transformatrions on drawables.';
+
+1;
diff --git a/pdb/groups/dynamics.pdb b/pdb/groups/dynamics.pdb
new file mode 100644
index 0000000..db79881
--- /dev/null
+++ b/pdb/groups/dynamics.pdb
@@ -0,0 +1,87 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub dynamics_refresh {
+    $blurb = 'Refresh current paint dynamics. This function always succeeds.';
+
+    $help = <<'HELP';
+This procedure retrieves all paint dynamics currently in the user's
+paint dynamics path and updates the paint dynamics dialogs
+accordingly.
+HELP
+
+    &mitch_pdb_misc('2011', '2.8');
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_data_factory_data_refresh (gimp->dynamics_factory, context);
+}
+CODE
+    );
+}
+
+sub dynamics_get_list {
+    $blurb = 'Retrieve the list of loaded paint dynamics.';
+
+    $help = <<'HELP';
+This procedure returns a list of the paint dynamics that are currently
+available.
+HELP
+
+    &mitch_pdb_misc('2011', '2.8');
+
+    @inargs = (
+       { name => 'filter', type => 'string', null_ok => 1,
+          desc => 'An optional regular expression used to filter the list' }
+    );
+
+    @outargs = (
+       { name => 'dynamics_list', type => 'stringarray',
+         desc => 'The list of paint dynamics names',
+         array => { name => 'num_dynamics',
+                    desc => 'The number of available paint dynamics' } }
+    );
+
+    %invoke = (
+        headers => [ qw("core/gimpcontainer-filter.h") ],
+       code => <<'CODE'
+{
+  dynamics_list = gimp_container_get_filtered_name_array (gimp_data_factory_get_container 
(gimp->dynamics_factory),
+                                                          filter, &num_dynamics);
+}
+CODE
+    );
+}
+
+
+@headers = qw("core/gimp.h"
+              "core/gimpcontainer.h"
+              "core/gimpdatafactory.h");
+
+@procs = qw(dynamics_refresh
+            dynamics_get_list);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Paint Dynamics';
+$doc_title = 'gimpdynamics';
+$doc_short_desc = 'Operations related to paint dynamics.';
+$doc_long_desc = 'Operations related to paint dynamics.';
+
+1;
diff --git a/pdb/groups/edit.pdb b/pdb/groups/edit.pdb
new file mode 100644
index 0000000..06ac9ad
--- /dev/null
+++ b/pdb/groups/edit.pdb
@@ -0,0 +1,1052 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub edit_cut {
+    $blurb = 'Cut from the specified drawable.';
+
+    $help = <<'HELP';
+If there is a selection in the image, then the area specified by the
+selection is cut from the specified drawable and placed in an internal
+GIMP edit buffer. It can subsequently be retrieved using the
+gimp_edit_paste() command. If there is no selection, then the
+specified drawable will be removed and its contents stored in the
+internal GIMP edit buffer. This procedure will fail if the selected area
+lies completely outside the bounds of the current drawable and there is
+nothing to copy from.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable to cut from' }
+    );
+
+    @outargs = (
+       { name => 'non_empty', type => 'boolean',
+         desc => 'TRUE if the cut was successful,
+                   FALSE if there was nothing to copy from' }
+    );
+
+    %invoke = (
+       code => <<CODE
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GimpImage *image    = gimp_item_get_image (GIMP_ITEM (drawable));
+      GError    *my_error = NULL;
+
+      non_empty = gimp_edit_cut (image, drawable, context, &my_error) != NULL;
+
+      if (! non_empty)
+        {
+          gimp_message_literal (gimp,
+                                G_OBJECT (progress), GIMP_MESSAGE_WARNING,
+                                my_error->message);
+          g_clear_error (&my_error);
+        }
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub edit_copy {
+    $blurb = 'Copy from the specified drawable.';
+
+    $help = <<'HELP';
+If there is a selection in the image, then the area specified by the
+selection is copied from the specified drawable and placed in an
+internal GIMP edit buffer. It can subsequently be retrieved using the
+gimp_edit_paste() command.  If there is no selection, then the
+specified drawable's contents will be stored in the internal GIMP edit
+buffer. This procedure will fail if the selected area lies completely
+outside the bounds of the current drawable and there is nothing to
+copy from.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable to copy from' }
+    );
+
+    @outargs = (
+       { name => 'non_empty', type => 'boolean',
+         desc => 'TRUE if the cut was successful,
+                   FALSE if there was nothing to copy from' }
+    );
+
+    %invoke = (
+       code => <<CODE
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL, 0, error))
+    {
+      GimpImage *image    = gimp_item_get_image (GIMP_ITEM (drawable));
+      GError    *my_error = NULL;
+
+      non_empty = gimp_edit_copy (image, drawable, context, &my_error) != NULL;
+
+      if (! non_empty)
+        {
+          gimp_message_literal (gimp,
+                                G_OBJECT (progress), GIMP_MESSAGE_WARNING,
+                                my_error->message);
+          g_clear_error (&my_error);
+        }
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub edit_copy_visible {
+    $blurb = 'Copy from the projection.';
+
+    $help = <<'HELP';
+If there is a selection in the image, then the area specified by the
+selection is copied from the projection and placed in an internal GIMP
+edit buffer. It can subsequently be retrieved using the
+gimp_edit_paste() command. If there is no selection, then the
+projection's contents will be stored in the internal GIMP edit buffer.
+HELP
+
+    &mitch_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => "The image to copy from" }
+    );
+
+    @outargs = (
+       { name => 'non_empty', type => 'boolean',
+         desc => 'TRUE if the copy was successful' }
+    );
+
+    %invoke = (
+       code => <<CODE
+{
+  GError *my_error = NULL;
+
+  non_empty = gimp_edit_copy_visible (image, context, &my_error) != NULL;
+
+  if (! non_empty)
+    {
+      gimp_message_literal (gimp,
+                            G_OBJECT (progress), GIMP_MESSAGE_WARNING,
+                            my_error->message);
+      g_clear_error (&my_error);
+    }
+}
+CODE
+    );
+}
+
+sub edit_paste {
+    $blurb = 'Paste buffer to the specified drawable.';
+
+    $help = <<'HELP';
+This procedure pastes a copy of the internal GIMP edit buffer to the
+specified drawable. The GIMP edit buffer will be empty unless a call
+was previously made to either gimp_edit_cut() or gimp_edit_copy(). The
+"paste_into" option specifies whether to clear the current image
+selection, or to paste the buffer "behind" the selection. This allows
+the selection to act as a mask for the pasted buffer. Anywhere that
+the selection mask is non-zero, the pasted buffer will show
+through. The pasted buffer will be a new layer in the image which is
+designated as the image floating selection. If the image has a
+floating selection at the time of pasting, the old floating selection
+will be anchored to its drawable before the new floating selection is
+added. This procedure returns the new floating layer. The resulting
+floating selection will already be attached to the specified drawable,
+and a subsequent call to floating_sel_attach is not needed.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable to paste to' },
+        { name => 'paste_into', type => 'boolean',
+         desc => 'Clear selection, or paste behind it?' }
+    );
+
+    @outargs = (
+       { name  => 'floating_sel', type  => 'layer',
+         desc  => 'The new floating selection' }
+    );
+
+    %invoke = (
+       code => <<CODE
+{
+  GimpObject *paste = gimp_get_clipboard_object (gimp);
+
+  if (paste &&
+      gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      floating_sel = gimp_edit_paste (gimp_item_get_image (GIMP_ITEM (drawable)),
+                                      drawable, paste,
+                                      paste_into ?
+                                      GIMP_PASTE_TYPE_FLOATING_INTO :
+                                      GIMP_PASTE_TYPE_FLOATING,
+                                      -1, -1, -1, -1);
+
+      if (! floating_sel)
+        success = FALSE;
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub edit_paste_as_new_image {
+    $blurb = 'Paste buffer to a new image.';
+
+    $help = <<'HELP';
+This procedure pastes a copy of the internal GIMP edit buffer to a new
+image.  The GIMP edit buffer will be empty unless a call was
+previously made to either gimp_edit_cut() or gimp_edit_copy(). This
+procedure returns the new image or -1 if the edit buffer was empty.
+HELP
+
+    &mitch_pdb_misc('2005', '2.10');
+
+    @outargs = (
+       { name => 'image', type => 'image',
+         desc => 'The new image' }
+    );
+    %invoke = (
+       code => <<CODE
+{
+  GimpObject *paste = gimp_get_clipboard_object (gimp);
+
+  if (paste)
+    {
+      image = gimp_edit_paste_as_new_image (gimp, paste);
+
+      if (! image)
+        success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub edit_named_cut {
+    $blurb = 'Cut into a named buffer.';
+
+    $help = <<'HELP';
+This procedure works like gimp_edit_cut(), but additionally stores the
+cut buffer into a named buffer that will stay available for later
+pasting, regardless of any intermediate copy or cut operations.
+HELP
+
+    &mitch_pdb_misc('2005', '2.4');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => "The drawable to cut from" },
+        { name => 'buffer_name', type => 'string', non_empty => 1,
+          desc => 'The name of the buffer to create' }
+    );
+
+    @outargs = (
+       { name => 'real_name', type => 'string',
+         desc => 'The real name given to the buffer, or NULL if the
+                   cut failed' }
+    );
+
+    %invoke = (
+       code => <<CODE
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GimpImage *image    = gimp_item_get_image (GIMP_ITEM (drawable));
+      GError    *my_error = NULL;
+
+      real_name = (gchar *) gimp_edit_named_cut (image, buffer_name,
+                                                 drawable, context, &my_error);
+
+      if (real_name)
+        {
+          real_name = g_strdup (real_name);
+        }
+      else
+        {
+          gimp_message_literal (gimp,
+                                G_OBJECT (progress), GIMP_MESSAGE_WARNING,
+                                my_error->message);
+          g_clear_error (&my_error);
+        }
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub edit_named_copy {
+    $blurb = 'Copy into a named buffer.';
+
+    $help = <<'HELP';
+This procedure works like gimp_edit_copy(), but additionally stores the
+copied buffer into a named buffer that will stay available for later
+pasting, regardless of any intermediate copy or cut operations.
+HELP
+
+    &mitch_pdb_misc('2005', '2.4');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => "The drawable to copy from" },
+        { name => 'buffer_name', type => 'string', non_empty => 1,
+          desc => 'The name of the buffer to create' }
+    );
+
+    @outargs = (
+       { name => 'real_name', type => 'string',
+         desc => 'The real name given to the buffer, or NULL if the
+                   copy failed' }
+    );
+
+    %invoke = (
+       code => <<CODE
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL, 0, error))
+    {
+      GimpImage *image    = gimp_item_get_image (GIMP_ITEM (drawable));
+      GError    *my_error = NULL;
+
+      real_name = (gchar *) gimp_edit_named_copy (image, buffer_name,
+                                                  drawable, context, &my_error);
+
+      if (real_name)
+        {
+          real_name = g_strdup (real_name);
+        }
+      else
+        {
+          gimp_message_literal (gimp,
+                                G_OBJECT (progress), GIMP_MESSAGE_WARNING,
+                                my_error->message);
+          g_clear_error (&my_error);
+        }
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub edit_named_copy_visible {
+    $blurb = 'Copy from the projection into a named buffer.';
+
+    $help = <<'HELP';
+This procedure works like gimp_edit_copy_visible(), but additionally
+stores the copied buffer into a named buffer that will stay available
+for later pasting, regardless of any intermediate copy or cut
+operations.
+HELP
+
+    &mitch_pdb_misc('2005', '2.4');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => "The image to copy from" },
+        { name => 'buffer_name', type => 'string', non_empty => 1,
+          desc => 'The name of the buffer to create' }
+    );
+
+    @outargs = (
+       { name => 'real_name', type => 'string',
+         desc => 'The real name given to the buffer, or NULL if the
+                   copy failed' }
+    );
+
+    %invoke = (
+       code => <<CODE
+{
+  GError *my_error = NULL;
+
+  real_name = (gchar *) gimp_edit_named_copy_visible (image, buffer_name,
+                                                      context, &my_error);
+
+  if (real_name)
+    {
+      real_name = g_strdup (real_name);
+    }
+  else
+    {
+      gimp_message_literal (gimp,
+                            G_OBJECT (progress), GIMP_MESSAGE_WARNING,
+                            my_error->message);
+      g_clear_error (&my_error);
+    }
+}
+CODE
+    );
+}
+
+sub edit_named_paste {
+    $blurb = 'Paste named buffer to the specified drawable.';
+
+    $help = <<'HELP';
+This procedure works like gimp_edit_paste() but pastes a named buffer
+instead of the global buffer.
+HELP
+
+    &mitch_pdb_misc('2005', '2.4');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable to paste to' },
+        { name => 'buffer_name', type => 'string',
+          desc => 'The name of the buffer to paste' },
+        { name => 'paste_into', type => 'boolean',
+         desc => 'Clear selection, or paste behind it?' }
+    );
+
+    @outargs = (
+       { name  => 'floating_sel', type  => 'layer',
+         desc  => 'The new floating selection' }
+    );
+
+    %invoke = (
+       code => <<CODE
+{
+  GimpBuffer *buffer = gimp_pdb_get_buffer (gimp, buffer_name, error);
+
+  if (buffer &&
+      gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      floating_sel = gimp_edit_paste (gimp_item_get_image (GIMP_ITEM (drawable)),
+                                      drawable, GIMP_OBJECT (buffer),
+                                      paste_into ?
+                                      GIMP_PASTE_TYPE_FLOATING_INTO :
+                                      GIMP_PASTE_TYPE_FLOATING,
+                                      -1, -1, -1, -1);
+      if (! floating_sel)
+        success = FALSE;
+    }
+  else
+    success = FALSE;
+}
+CODE
+    )
+}
+
+sub edit_named_paste_as_new_image {
+    $blurb = 'Paste named buffer to a new image.';
+
+    $help = <<'HELP';
+This procedure works like gimp_edit_paste_as_new_image() but pastes a
+named buffer instead of the global buffer.
+HELP
+
+    &mitch_pdb_misc('2005', '2.10');
+
+    @inargs = (
+        { name => 'buffer_name', type => 'string',
+          desc => 'The name of the buffer to paste' }
+    );
+
+    @outargs = (
+       { name => 'image', type => 'image',
+         desc => 'The new image' }
+    );
+
+    %invoke = (
+       code => <<CODE
+{
+  GimpBuffer *buffer = gimp_pdb_get_buffer (gimp, buffer_name, error);
+
+  if (buffer)
+    {
+      image = gimp_edit_paste_as_new_image (gimp, GIMP_OBJECT (buffer));
+
+      if (! image)
+        success = FALSE;
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub edit_clear {
+    $blurb = 'Clear selected area of drawable.';
+
+    $help = <<'HELP';
+This procedure clears the specified drawable. If the drawable has an
+alpha channel, the cleared pixels will become transparent. If the
+drawable does not have an alpha channel, cleared pixels will be set to
+the background color. This procedure only affects regions within a
+selection if there is a selection active.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable to clear from' }
+    );
+
+    %invoke = (
+       code => <<CODE
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable));
+
+      gimp_edit_clear (image, drawable, context);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub edit_fill {
+    $blurb = 'Fill selected area of drawable.';
+
+    $help = <<'HELP';
+This procedure fills the specified drawable with the fill mode. If the
+fill mode is foreground, the current foreground color is used. If the
+fill mode is background, the current background color is used. Other
+fill modes should not be used. This procedure only affects regions
+within a selection if there is a selection active. If you want to fill
+the whole drawable, regardless of the selection, use
+gimp_drawable_fill().
+HELP
+
+    &std_pdb_misc;
+    $author .= ' & Raphael Quinet';
+    $date = '1995-2000';
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => "The drawable to fill to" },
+       { name => 'fill_type', type => 'enum GimpFillType',
+         desc => 'The type of fill' }
+    );
+
+    %invoke = (
+       code => <<CODE
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GimpImage       *image   = gimp_item_get_image (GIMP_ITEM (drawable));
+      GimpFillOptions *options = gimp_fill_options_new (gimp, NULL, FALSE);
+
+      if (gimp_fill_options_set_by_fill_type (options, context,
+                                              fill_type, error))
+        {
+          gimp_edit_fill (image, drawable, options, NULL);
+        }
+      else
+        success = FALSE;
+
+      g_object_unref (options);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub edit_bucket_fill {
+    $blurb = <<'BLURB';
+Fill the area specified either by the current selection if there is one, or by
+a seed fill starting at the specified coordinates.
+BLURB
+
+    $help = <<'HELP';
+This tool requires information on the paint application mode, and the
+fill mode, which can either be in the foreground color, or in the
+currently active pattern. If there is no selection, a seed fill is
+executed at the specified coordinates and extends outward in keeping
+with the threshold parameter. If there is a selection in the target
+image, the threshold, sample merged, x, and y arguments are unused. If
+the sample_merged parameter is TRUE, the data of the composite
+image will be used instead of that for the specified drawable.  This
+is equivalent to sampling for colors after merging all visible
+layers. In the case of merged sampling, the x and y coordinates are
+relative to the image's origin; otherwise, they are relative to the
+drawable's origin.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+       { name => 'fill_mode', type => 'enum GimpBucketFillMode',
+         desc => 'The type of fill' },
+       { name => paint_mode, type => 'enum GimpLayerMode',
+          default => 'GIMP_LAYER_MODE_NORMAL',
+         desc => 'The paint application mode' },
+       { name => 'opacity', type => '0 <= float <= 100',
+         desc => 'The opacity of the final bucket fill' },
+       { name => 'threshold', type => '0 <= float <= 255',
+         desc => "The threshold determines how extensive the seed fill will
+                  be. It's value is specified in terms of intensity levels.
+                   This parameter is only valid when there is no selection in
+                  the specified image." },
+       { name => 'sample_merged', type => 'boolean',
+         desc => 'Use the composite image, not the drawable' },
+       { name => 'x', type => 'float',
+         desc => "The x coordinate of this bucket fill's application.
+                  This parameter is only valid when there is no selection
+                  in the specified image." },
+       { name => 'y', type => 'float',
+         desc => "The y coordinate of this bucket fill's application.
+                  This parameter is only valid when there is no selection
+                  in the specified image." }
+    );
+
+    %invoke = (
+        headers => [ qw ("core/gimpdrawable-bucket-fill.h"
+                        "core/gimpchannel.h") ],
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GimpImage       *image   = gimp_item_get_image (GIMP_ITEM (drawable));
+      GimpFillOptions *options = gimp_fill_options_new (gimp, NULL, FALSE);
+
+      if (gimp_fill_options_set_by_fill_mode (options, context,
+                                              fill_mode, error))
+        {
+          if (paint_mode == GIMP_LAYER_MODE_OVERLAY_LEGACY)
+            paint_mode = GIMP_LAYER_MODE_SOFTLIGHT_LEGACY;
+
+          gimp_context_set_opacity (GIMP_CONTEXT (options), opacity / 100.0);
+          gimp_context_set_paint_mode (GIMP_CONTEXT (options), paint_mode);
+
+          if (! gimp_channel_is_empty (gimp_image_get_mask (image)))
+            {
+              gimp_edit_fill (image, drawable, options, NULL);
+            }
+          else
+            {
+              gimp_drawable_bucket_fill (drawable, options,
+                                         FALSE /* don't fill transparent */,
+                                         GIMP_SELECT_CRITERION_COMPOSITE,
+                                         threshold / 255.0,
+                                         sample_merged,
+                                         FALSE /* no diagonal neighbors */,
+                                         x, y);
+           }
+        }
+      else
+        success = FALSE;
+
+      g_object_unref (options);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub edit_bucket_fill_full {
+    $blurb = <<'BLURB';
+Fill the area specified either by the current selection if there is one, or by
+a seed fill starting at the specified coordinates.
+BLURB
+
+    $help = <<'HELP';
+This tool requires information on the paint application mode, and the
+fill mode, which can either be in the foreground color, or in the
+currently active pattern. If there is no selection, a seed fill is
+executed at the specified coordinates and extends outward in keeping
+with the threshold parameter. If there is a selection in the target
+image, the threshold, sample merged, x, and y arguments are unused. If
+the sample_merged parameter is TRUE, the data of the composite
+image will be used instead of that for the specified drawable.  This
+is equivalent to sampling for colors after merging all visible
+layers. In the case of merged sampling, the x and y coordinates are
+relative to the image's origin; otherwise, they are relative to the
+drawable's origin.
+HELP
+
+    &david_pdb_misc('2006', '2.4');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+       { name => 'fill_mode', type => 'enum GimpBucketFillMode',
+         desc => 'The type of fill' },
+       { name => paint_mode, type => 'enum GimpLayerMode',
+          default => 'GIMP_LAYER_MODE_NORMAL',
+         desc => 'The paint application mode' },
+       { name => 'opacity', type => '0 <= float <= 100',
+         desc => 'The opacity of the final bucket fill' },
+       { name => 'threshold', type => '0 <= float <= 255',
+         desc => "The threshold determines how extensive the seed fill will
+                  be. It's value is specified in terms of intensity levels.
+                   This parameter is only valid when there is no selection in
+                  the specified image." },
+       { name => 'sample_merged', type => 'boolean',
+         desc => 'Use the composite image, not the drawable' },
+       { name => 'fill_transparent', type => 'boolean',
+         desc => "Whether to consider transparent pixels for filling.
+                  If TRUE, transparency is considered as a unique fillable
+                  color." },
+       { name => 'select_criterion', type => 'enum GimpSelectCriterion',
+         desc => "The criterion used to determine color similarity.
+                  SELECT_CRITERION_COMPOSITE is the standard choice.
+                  " },
+       { name => 'x', type => 'float',
+         desc => "The x coordinate of this bucket fill's application.
+                  This parameter is only valid when there is no selection
+                  in the specified image." },
+       { name => 'y', type => 'float',
+         desc => "The y coordinate of this bucket fill's application.
+                  This parameter is only valid when there is no selection
+                  in the specified image." }
+    );
+
+    %invoke = (
+        headers => [ qw ("core/gimpdrawable-bucket-fill.h"
+                        "core/gimpchannel.h") ],
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GimpImage       *image   = gimp_item_get_image (GIMP_ITEM (drawable));
+      GimpFillOptions *options = gimp_fill_options_new (gimp, NULL, FALSE);
+
+      if (gimp_fill_options_set_by_fill_mode (options, context,
+                                              fill_mode, error))
+        {
+          if (paint_mode == GIMP_LAYER_MODE_OVERLAY_LEGACY)
+            paint_mode = GIMP_LAYER_MODE_SOFTLIGHT_LEGACY;
+
+          gimp_context_set_opacity (GIMP_CONTEXT (options), opacity / 100.0);
+          gimp_context_set_paint_mode (GIMP_CONTEXT (options), paint_mode);
+
+          if (! gimp_channel_is_empty (gimp_image_get_mask (image)))
+            {
+              gimp_edit_fill (image, drawable, options, NULL);
+            }
+          else
+            {
+              gimp_drawable_bucket_fill (drawable, options,
+                                         fill_transparent,
+                                         select_criterion,
+                                         threshold / 255.0,
+                                         sample_merged,
+                                         FALSE /* no diagonal neighbors */,
+                                         x, y);
+           }
+        }
+      else
+        success = FALSE;
+
+      g_object_unref (options);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub edit_blend {
+    $blurb = <<'BLURB';
+Blend between the starting and ending coordinates with the specified
+blend mode and gradient type.
+BLURB
+
+    $help = <<'HELP';
+This tool requires information on the paint application mode, the
+blend mode, and the gradient type. It creates the specified variety of
+blend using the starting and ending coordinates as defined for each
+gradient type.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+        { name => 'blend_mode', type => 'enum GimpBlendMode',
+         desc => 'The type of blend' },
+       { name => 'paint_mode', type => 'enum GimpLayerMode',
+          default => 'GIMP_LAYER_MODE_NORMAL',
+         desc => 'The paint application mode' },
+       { name => 'gradient_type',  type => 'enum GimpGradientType',
+         desc => 'The type of gradient' },
+       { name => 'opacity', type => '0 <= float <= 100',
+         desc => 'The opacity of the final blend' },
+       { name => 'offset', type => '0 <= float',
+         desc => 'Offset relates to the starting and ending coordinates
+                  specified for the blend. This parameter is mode dependent.' },
+       { name => 'repeat', type => 'enum GimpRepeatMode',
+         desc => 'Repeat mode' },
+       { name => 'reverse', type => 'boolean',
+         desc => 'Use the reverse gradient' },
+       { name => 'supersample', type => 'boolean',
+         desc => 'Do adaptive supersampling' },
+       { name => 'max_depth', type => '1 <= int32 <= 9', no_validate => 1,
+         desc => 'Maximum recursion levels for supersampling' },
+       { name => 'threshold', type => '0 <= float <= 4', no_validate => 1,
+         desc => 'Supersampling threshold' },
+       { name => 'dither', type => 'boolean',
+         desc => 'Use dithering to reduce banding' },
+       { name => 'x1', type => 'float',
+         desc => "The x coordinate of this blend's starting point" },
+       { name => 'y1', type => 'float',
+         desc => "The y coordinate of this blend's starting point" },
+       { name => 'x2', type => 'float',
+         desc => "The x coordinate of this blend's ending point" },
+       { name => 'y2', type => 'float',
+         desc => "The y coordinate of this blend's ending point" }
+    );
+
+    %invoke = (
+        headers => [ qw("core/gimp-gradients.h" "core/gimpdrawable-blend.h") ],
+       code => <<'CODE'
+{
+  success = (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                        GIMP_PDB_ITEM_CONTENT, error) &&
+             gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error));
+
+  if (success && supersample)
+    {
+      if (max_depth < 1 || max_depth > 9)
+        success = FALSE;
+
+      if (threshold < 0.0 || threshold > 4.0)
+        success = FALSE;
+    }
+
+  if (success)
+    {
+      GimpGradient *gradient;
+
+      if (paint_mode == GIMP_LAYER_MODE_OVERLAY_LEGACY)
+        paint_mode = GIMP_LAYER_MODE_SOFTLIGHT_LEGACY;
+
+      if (progress)
+        gimp_progress_start (progress, FALSE, _("Blending"));
+
+      switch (blend_mode)
+        {
+        case GIMP_BLEND_FG_BG_RGB:
+          gradient = gimp_gradients_get_fg_bg_rgb (context->gimp);
+          break;
+
+        case GIMP_BLEND_FG_BG_HSV:
+          gradient = gimp_gradients_get_fg_bg_hsv_cw (context->gimp);
+          break;
+
+        case GIMP_BLEND_FG_TRANSPARENT:
+          gradient = gimp_gradients_get_fg_transparent (context->gimp);
+          break;
+
+        case GIMP_BLEND_CUSTOM:
+       default:
+          gradient = gimp_context_get_gradient (context);
+          break;
+        }
+
+      gimp_drawable_blend (drawable,
+                           context,
+                           gradient,
+                           paint_mode,
+                           gradient_type,
+                           opacity / 100.0,
+                          offset, repeat, reverse,
+                           supersample, max_depth,
+                           threshold, dither,
+                           x1, y1, x2, y2,
+                          progress);
+
+      if (progress)
+        gimp_progress_end (progress);
+    }
+}
+CODE
+    );
+}
+
+sub edit_stroke {
+    $blurb = 'Stroke the current selection';
+
+    $help = <<'HELP';
+This procedure strokes the current selection, painting along the selection
+boundary with the active brush and foreground color. The paint is applied to
+the specified drawable regardless of the active selection.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable to stroke to' }
+    );
+
+    %invoke = (
+       headers => [ qw("core/gimpstrokeoptions.h") ],
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GimpImage         *image = gimp_item_get_image (GIMP_ITEM (drawable));
+      GimpStrokeOptions *options;
+      GimpPaintOptions  *paint_options;
+
+      options = gimp_pdb_context_get_stroke_options (GIMP_PDB_CONTEXT (context));
+
+      paint_options =
+        gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context), NULL);
+      paint_options = gimp_config_duplicate (GIMP_CONFIG (paint_options));
+
+      success = gimp_item_stroke (GIMP_ITEM (gimp_image_get_mask (image)),
+                                  drawable, context, options, paint_options,
+                                  TRUE, progress, error);
+
+      g_object_unref (paint_options);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub edit_stroke_vectors {
+    $blurb = 'Stroke the specified vectors object';
+
+    $help = <<'HELP';
+This procedure strokes the specified vectors object, painting along the
+path with the active brush and foreground color.
+HELP
+
+    &simon_pdb_misc('2006', '2.4');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable to stroke to' },
+       { name => 'vectors', type => 'vectors',
+          desc => 'The vectors object' }
+    );
+
+    %invoke = (
+       headers => [ qw("core/gimpstrokeoptions.h") ],
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error) &&
+      gimp_pdb_item_is_attached (GIMP_ITEM (vectors),
+                                 gimp_item_get_image (GIMP_ITEM (drawable)),
+                                 0, error))
+    {
+      GimpStrokeOptions *options;
+      GimpPaintOptions  *paint_options;
+
+      options = gimp_pdb_context_get_stroke_options (GIMP_PDB_CONTEXT (context));
+
+      paint_options =
+        gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context), NULL);
+      paint_options = gimp_config_duplicate (GIMP_CONFIG (paint_options));
+
+      success = gimp_item_stroke (GIMP_ITEM (vectors),
+                                  drawable, context, options, paint_options,
+                                  TRUE, progress, error);
+
+      g_object_unref (paint_options);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+@headers = qw("libgimpconfig/gimpconfig.h"
+              "core/gimp.h"
+              "core/gimp-edit.h"
+              "core/gimpbuffer.h"
+              "core/gimpimage.h"
+              "core/gimpprogress.h"
+              "gimppdb-utils.h"
+              "gimppdbcontext.h"
+              "gimp-intl.h");
+
+@procs = qw(edit_cut
+            edit_copy
+            edit_copy_visible
+            edit_paste
+            edit_paste_as_new_image
+            edit_named_cut
+            edit_named_copy
+            edit_named_copy_visible
+            edit_named_paste
+            edit_named_paste_as_new_image
+            edit_clear
+            edit_fill
+            edit_bucket_fill
+            edit_bucket_fill_full
+           edit_blend
+            edit_stroke
+            edit_stroke_vectors);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Edit procedures';
+$doc_title = 'gimpedit';
+$doc_short_desc = 'Edit menu functions (cut, copy, paste, clear, etc.)';
+$doc_long_desc = 'Edit menu functions (cut, copy, paste, clear, etc.)';
+
+1;
diff --git a/pdb/groups/fileops.pdb b/pdb/groups/fileops.pdb
new file mode 100644
index 0000000..240696d
--- /dev/null
+++ b/pdb/groups/fileops.pdb
@@ -0,0 +1,720 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995, 1996, 1997 Spencer Kimball and Peter Mattis
+# Copyright (C) 1997 Josh MacDonald
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub file_load {
+    $blurb = 'Loads an image file by invoking the right load handler.';
+
+    $help = <<'HELP';
+This procedure invokes the correct file load handler using magic if possible,
+and falling back on the file's extension and/or prefix if not. The name of
+the file to load is typically a full pathname, and the name entered is what
+the user actually typed before prepending a directory path. The reason for
+this is that if the user types http://www.xcf/~gimp/ he wants to fetch a URL,
+and the full pathname will not look like a URL.
+HELP
+
+    &josh_pdb_misc('1997');
+
+    @inargs = (
+        { name => 'run_mode',
+          type => 'enum GimpRunMode (no GIMP_RUN_WITH_LAST_VALS)',
+          desc => 'The run mode' },
+        { name => 'filename', type => 'string', allow_non_utf8 => 1,
+          desc => 'The name of the file to load' },
+        { name => 'raw_filename', type => 'string', allow_non_utf8 => 1,
+          desc => 'The name as entered by the user' }
+    );
+
+    @outargs = (
+        { name => 'image', type => 'image',
+          desc => 'The output image' }
+    );
+
+    %invoke = (
+        no_marshalling => 1,
+        code => <<'CODE'
+{
+  GimpValueArray      *new_args;
+  GimpValueArray      *return_vals;
+  GimpPlugInProcedure *file_proc;
+  GimpProcedure       *proc;
+  GFile               *file;
+  gint                 i;
+
+  file = file_utils_filename_to_file (gimp,
+                                      g_value_get_string (gimp_value_array_index (args, 1)),
+                                      error);
+
+  if (! file)
+    return gimp_procedure_get_return_values (procedure, FALSE,
+                                             error ? *error : NULL);
+
+  file_proc = gimp_plug_in_manager_file_procedure_find (gimp->plug_in_manager,
+                                                        GIMP_FILE_PROCEDURE_GROUP_OPEN,
+                                                        file, error);
+
+  if (! file_proc)
+    {
+      g_object_unref (file);
+
+      return gimp_procedure_get_return_values (procedure, FALSE,
+                                               error ? *error : NULL);
+    }
+
+  proc = GIMP_PROCEDURE (file_proc);
+
+  new_args = gimp_procedure_get_arguments (proc);
+
+  g_value_transform (gimp_value_array_index (args, 0),
+                    gimp_value_array_index (new_args, 0));
+
+  if (file_proc->handles_uri)
+    g_value_take_string (gimp_value_array_index (new_args, 1),
+                         g_file_get_uri (file));
+  else
+    g_value_transform (gimp_value_array_index (args, 1),
+                       gimp_value_array_index (new_args, 1));
+
+  g_value_transform (gimp_value_array_index (args, 2),
+                     gimp_value_array_index (new_args, 2));
+
+  for (i = 3; i < proc->num_args; i++)
+    if (G_IS_PARAM_SPEC_STRING (proc->args[i]))
+      g_value_set_static_string (gimp_value_array_index (new_args, i), "");
+
+  return_vals =
+    gimp_pdb_execute_procedure_by_name_args (gimp->pdb,
+                                             context, progress, error,
+                                             gimp_object_get_name (proc),
+                                             new_args);
+
+  gimp_value_array_unref (new_args);
+
+  if (g_value_get_enum (gimp_value_array_index (return_vals, 0)) ==
+      GIMP_PDB_SUCCESS)
+    {
+      if (gimp_value_array_length (return_vals) > 1 &&
+          GIMP_VALUE_HOLDS_IMAGE_ID (gimp_value_array_index (return_vals, 1)))
+        {
+          GimpImage *image =
+            gimp_value_get_image (gimp_value_array_index (return_vals, 1),
+                                  gimp);
+          gimp_image_set_load_proc (image, file_proc);
+        }
+    }
+
+  g_object_unref (file);
+
+  return return_vals;
+}
+CODE
+    );
+}
+
+sub file_load_layer {
+    $blurb = 'Loads an image file as a layer for an existing image.';
+
+    $help = <<'HELP';
+This procedure behaves like the file-load procedure but opens the specified
+image as a layer for an existing image. The returned layer needs to be
+added to the existing image with gimp_image_insert_layer().
+HELP
+
+    &neo_pdb_misc('2005', '2.4');
+
+    @inargs = (
+        { name => 'run_mode',
+          type => 'enum GimpRunMode (no GIMP_RUN_WITH_LAST_VALS)',
+          desc => 'The run mode' },
+        { name => 'image', type => 'image',
+          desc => 'Destination image' },
+        { name => 'filename', type => 'string', allow_non_utf8 => 1,
+          desc => 'The name of the file to load' }
+    );
+
+    @outargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The layer created when loading the image file' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GFile *file = file_utils_filename_to_file (gimp, filename, error);
+
+  if (file)
+    {
+      GList             *layers;
+      GimpPDBStatusType  status;
+
+      layers = file_open_layers (gimp, context, progress,
+                                 image, FALSE,
+                                 file, run_mode, NULL, &status, error);
+
+      g_object_unref (file);
+
+      if (layers)
+        {
+          layer = layers->data;
+          g_list_free (layers);
+        }
+      else
+        success = FALSE;
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub file_load_layers {
+    $blurb = 'Loads an image file as layers for an existing image.';
+
+    $help = <<'HELP';
+This procedure behaves like the file-load procedure but opens the specified
+image as layers for an existing image. The returned layers needs to be
+added to the existing image with gimp_image_insert_layer().
+HELP
+
+    &mitch_pdb_misc('2006', '2.4');
+
+    @inargs = (
+        { name => 'run_mode',
+          type => 'enum GimpRunMode (no GIMP_RUN_WITH_LAST_VALS)',
+          desc => 'The run mode' },
+        { name => 'image', type => 'image',
+          desc => 'Destination image' },
+        { name => 'filename', type => 'string', allow_non_utf8 => 1,
+          desc => 'The name of the file to load' }
+    );
+
+    @outargs = (
+        { name => 'layer_ids', type => 'int32array',
+          desc => 'The list of loaded layers',
+          array => { name => 'num_layers',
+                     desc => 'The number of loaded layers' } }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GFile *file = file_utils_filename_to_file (gimp, filename, error);
+
+  if (file)
+    {
+      GList             *layers;
+      GimpPDBStatusType  status;
+
+      layers = file_open_layers (gimp, context, progress,
+                                 image, FALSE,
+                                 file, run_mode, NULL, &status, error);
+
+      g_object_unref (file);
+
+      if (layers)
+        {
+          GList *list;
+          gint i;
+
+          num_layers = g_list_length (layers);
+
+          layer_ids = g_new (gint32, num_layers);
+
+          for (i = 0, list = layers;
+               i < num_layers;
+               i++, list = g_list_next (list))
+            layer_ids[i] = gimp_item_get_ID (GIMP_ITEM (list->data));
+
+          g_list_free (layers);
+        }
+      else
+        success = FALSE;
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub file_save {
+    $blurb = 'Saves a file by extension.';
+
+    $help = <<'HELP';
+This procedure invokes the correct file save handler according to the file's
+extension and/or prefix. The name of the file to save is typically a full
+pathname, and the name entered is what the user actually typed before
+prepending a directory path. The reason for this is that if the user types
+http://www.xcf/~gimp/ she wants to fetch a URL, and the full pathname will not
+look like a URL.
+HELP
+
+    &josh_pdb_misc('1997');
+
+    @inargs = (
+        { name => 'run_mode', type => 'enum GimpRunMode',
+          desc => 'The run mode' },
+        { name => 'image', type => 'image',
+          desc => 'Input image' },
+        { name => 'drawable', type => 'drawable',
+          desc => 'Drawable to save' },
+        { name => 'filename', type => 'string', allow_non_utf8 => 1,
+          desc => 'The name of the file to save the image in' },
+        { name => 'raw_filename', type => 'string', allow_non_utf8 => 1,
+          desc => 'The name as entered by the user' }
+    );
+
+    %invoke = (
+        headers => [ qw(<string.h>) ],
+        no_marshalling => 1,
+        code => <<'CODE'
+{
+  GimpValueArray      *new_args;
+  GimpValueArray      *return_vals;
+  GimpPlugInProcedure *file_proc;
+  GimpProcedure       *proc;
+  GFile               *file;
+  gint                 i;
+
+  file = file_utils_filename_to_file (gimp,
+                                      g_value_get_string (gimp_value_array_index (args, 3)),
+                                      error);
+
+  if (! file)
+    return gimp_procedure_get_return_values (procedure, FALSE,
+                                             error ? *error : NULL);
+
+  file_proc = gimp_plug_in_manager_file_procedure_find (gimp->plug_in_manager,
+                                                        GIMP_FILE_PROCEDURE_GROUP_SAVE,
+                                                        file, NULL);
+
+  if (! file_proc)
+    file_proc = gimp_plug_in_manager_file_procedure_find (gimp->plug_in_manager,
+                                                          GIMP_FILE_PROCEDURE_GROUP_EXPORT,
+                                                          file, error);
+
+  if (! file_proc)
+    {
+      g_object_unref (file);
+
+      return gimp_procedure_get_return_values (procedure, FALSE,
+                                               error ? *error : NULL);
+    }
+
+  proc = GIMP_PROCEDURE (file_proc);
+
+  new_args = gimp_procedure_get_arguments (proc);
+
+  g_value_transform (gimp_value_array_index (args, 0),
+                     gimp_value_array_index (new_args, 0));
+  g_value_transform (gimp_value_array_index (args, 1),
+                     gimp_value_array_index (new_args, 1));
+  g_value_transform (gimp_value_array_index (args, 2),
+                     gimp_value_array_index (new_args, 2));
+
+  if (file_proc->handles_uri)
+    g_value_take_string (gimp_value_array_index (new_args, 3),
+                         g_file_get_uri (file));
+  else
+    g_value_transform (gimp_value_array_index (args, 3),
+                       gimp_value_array_index (new_args, 3));
+
+  g_value_transform (gimp_value_array_index (args, 4),
+                     gimp_value_array_index (new_args, 4));
+
+  for (i = 5; i < proc->num_args; i++)
+    if (G_IS_PARAM_SPEC_STRING (proc->args[i]))
+      g_value_set_static_string (gimp_value_array_index (new_args, i), "");
+
+  return_vals =
+    gimp_pdb_execute_procedure_by_name_args (gimp->pdb,
+                                             context, progress, error,
+                                             gimp_object_get_name (proc),
+                                             new_args);
+
+  gimp_value_array_unref (new_args);
+
+  g_object_unref (file);
+
+  return return_vals;
+}
+CODE
+    );
+}
+
+sub file_load_thumbnail {
+    $blurb = 'Loads the thumbnail for a file.';
+
+    $help = <<'HELP';
+This procedure tries to load a thumbnail that belongs to the file with
+the given filename. This name is a full pathname. The returned data is 
+an array of colordepth 3 (RGB), regardless of the image type. Width and 
+height of the thumbnail are also returned. Don't use this function if 
+you need a thumbnail of an already opened image, use gimp_image_thumbnail()
+instead.
+HELP
+
+    $author = $copyright = 'Adam D. Moss, Sven Neumann';
+    $date = '1999-2003';
+
+    @inargs = (
+        { name => 'filename', type => 'string', allow_non_utf8 => 1,
+          desc => 'The name of the file that owns the thumbnail to load' }
+    );
+
+    @outargs = (
+        { name => 'width', type => 'int32',
+          desc => 'The width of the thumbnail' },
+        { name => 'height', type => 'int32',
+          desc => 'The height of the thumbnail' },
+        { name => 'thumb_data', type => 'int8array',
+          desc => 'The thumbnail data',
+          array =>  { name => 'thumb_data_count',
+                      desc => 'The number of bytes in thumbnail data' } }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GdkPixbuf *pixbuf = file_utils_load_thumbnail (filename);
+
+  if (pixbuf)
+    {
+      width            = gdk_pixbuf_get_width (pixbuf);
+      height           = gdk_pixbuf_get_height (pixbuf);
+      thumb_data_count = 3 * width * height;
+      thumb_data       = g_memdup (gdk_pixbuf_get_pixels (pixbuf),
+                                   thumb_data_count);
+
+      g_object_unref (pixbuf);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub file_save_thumbnail {
+    $blurb = 'Saves a thumbnail for the given image';
+
+    $help = <<'HELP';
+This procedure saves a thumbnail for the given image according to the
+Free Desktop Thumbnail Managing Standard. The thumbnail is saved so
+that it belongs to the file with the given filename. This means you
+have to save the image under this name first, otherwise this procedure
+will fail.  This procedure may become useful if you want to
+explicitly save a thumbnail with a file.
+HELP
+
+    &josh_pdb_misc('1997');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'filename', type => 'string', allow_non_utf8 => 1,
+          desc => 'The name of the file the thumbnail belongs to' },
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  success = file_utils_save_thumbnail (image, filename);
+}
+CODE
+    );
+}
+
+sub register_magic_load_handler {
+    $blurb = 'Registers a file load handler procedure.';
+
+    $help = <<'HELP';
+Registers a procedural database procedure to be called to load files of a 
+particular file format using magic file information.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'procedure_name', type => 'string', non_empty => 1,
+          desc => 'The name of the procedure to be used for loading' },
+        { name => 'extensions', type => 'string', no_validate => 1,
+          desc => 'comma separated list of extensions this handler
+                   can load (i.e. "jpg,jpeg")' },
+        { name => 'prefixes', type => 'string', no_validate => 1,
+          desc => 'comma separated list of prefixes this handler
+                   can load (i.e. "http:,ftp:")' },
+        { name => 'magics', type => 'string', no_validate => 1,
+          desc => 'comma separated list of magic file information
+                   this handler can load (i.e. "0,string,GIF")' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  gchar *canonical = gimp_canonicalize_identifier (procedure_name);
+
+  success = gimp_plug_in_manager_register_load_handler (gimp->plug_in_manager,
+                                                        canonical,
+                                                        extensions, prefixes, magics);
+
+  g_free (canonical);
+}
+CODE
+    );
+}
+
+sub register_load_handler {
+    $blurb = 'Registers a file load handler procedure.';
+
+    $help = <<'HELP';
+Registers a procedural database procedure to be called to load files of a
+particular file format.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'procedure_name', type => 'string', non_empty => 1,
+          desc => 'The name of the procedure to be used for loading' },
+        { name => 'extensions', type => 'string', no_validate => 1,
+          desc => 'comma separated list of extensions this handler
+                   can load (i.e. "jpg,jpeg")' },
+        { name => 'prefixes', type => 'string', no_validate => 1,
+          desc => 'comma separated list of prefixes this handler
+                   can load (i.e. "http:,ftp:")' }
+    );
+
+
+    %invoke = (
+        code => <<'CODE'
+{
+  gchar *canonical = gimp_canonicalize_identifier (procedure_name);
+
+  success = gimp_plug_in_manager_register_load_handler (gimp->plug_in_manager,
+                                                        canonical,
+                                                        extensions, prefixes, NULL);
+
+  g_free (canonical);
+}
+CODE
+    );
+}
+
+sub register_save_handler {
+    $blurb = 'Registers a file save handler procedure.';
+
+    $help = <<'HELP';
+Registers a procedural database procedure to be called to save files in a
+particular file format.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'procedure_name', type => 'string', non_empty => 1,
+          desc => 'The name of the procedure to be used for saving' },
+        { name => 'extensions', type => 'string', no_validate => 1,
+          desc => 'comma separated list of extensions this handler
+                   can save (i.e. "jpg,jpeg")' },
+        { name => 'prefixes', type => 'string', no_validate => 1,
+          desc => 'comma separated list of prefixes this handler
+                   can save (i.e. "http:,ftp:")' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  gchar *canonical = gimp_canonicalize_identifier (procedure_name);
+
+  success = gimp_plug_in_manager_register_save_handler (gimp->plug_in_manager,
+                                                        canonical,
+                                                        extensions, prefixes);
+
+  g_free (canonical);
+}
+CODE
+    );
+}
+
+sub register_file_handler_uri {
+    $blurb = 'Registers a file handler procedure as capable of handling URIs.';
+
+    $help = <<'HELP';
+Registers a file handler procedure as capable of handling URIs. This
+allows GIMP to call the procecure directly for all kinds of URIs, and
+the 'filename' traditionally passed to file procesures turns into an
+URI.
+HELP
+
+    &mitch_pdb_misc('2012', '2.10');
+
+    @inargs = (
+        { name => 'procedure_name', type => 'string', non_empty => 1,
+          desc => "The name of the procedure to enable URIs for." }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  gchar *canonical = gimp_canonicalize_identifier (procedure_name);
+
+  success = gimp_plug_in_manager_register_handles_uri (gimp->plug_in_manager,
+                                                       canonical);
+
+  g_free (canonical);
+}
+CODE
+    );
+}
+
+sub register_file_handler_mime {
+    $blurb = 'Associates MIME types with a file handler procedure.';
+
+    $help = <<'HELP';
+Registers MIME types for a file handler procedure. This allows GIMP to
+determine the MIME type of the file opened or saved using this
+procedure. It is recommended that only one MIME type is registered per
+file procedure; when registering more than one MIME type, GIMP will
+associate the first one with files opened or saved with this procedure.
+HELP
+
+    &neo_pdb_misc('2004', '2.2');
+
+    @inargs = (
+        { name => 'procedure_name', type => 'string', non_empty => 1,
+          desc => "The name of the procedure to associate a MIME type with." },
+        { name => 'mime_types', type => 'string',
+          desc => 'A comma-separated list of MIME types, such as "image/jpeg".' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  gchar *canonical = gimp_canonicalize_identifier (procedure_name);
+
+  success = gimp_plug_in_manager_register_mime_types (gimp->plug_in_manager,
+                                                      canonical, mime_types);
+
+  g_free (canonical);
+}
+CODE
+    );
+}
+
+sub register_file_handler_raw {
+    $blurb = 'Registers a file handler procedure as capable of handling raw camera files.';
+
+    $help = <<'HELP';
+Registers a file handler procedure as capable of handling raw digital
+camera files. Use this procedure only to register raw load handlers,
+calling it on a save handler will generate an error.
+HELP
+
+    &mitch_pdb_misc('2017', '2.10');
+
+    @inargs = (
+        { name => 'procedure_name', type => 'string', non_empty => 1,
+          desc => "The name of the procedure to enable raw handling for." }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  gchar *canonical = gimp_canonicalize_identifier (procedure_name);
+
+  success = gimp_plug_in_manager_register_handles_raw (gimp->plug_in_manager,
+                                                       canonical);
+
+  g_free (canonical);
+}
+CODE
+    );
+}
+
+sub register_thumbnail_loader {
+    $blurb = 'Associates a thumbnail loader with a file load procedure.';
+
+    $help = <<'HELP';
+Some file formats allow for embedded thumbnails, other file formats
+contain a scalable image or provide the image data in different
+resolutions. A file plug-in for such a format may register a special
+procedure that allows GIMP to load a thumbnail preview of the
+image. This procedure is then associated with the standard load
+procedure using this function.
+HELP
+
+    &neo_pdb_misc('2004', '2.2');
+
+    @inargs = (
+        { name => 'load_proc', type => 'string', non_empty => 1,
+          desc => "The name of the procedure the thumbnail loader with." },
+        { name => 'thumb_proc', type => 'string', non_empty => 1,
+          desc => "The name of the thumbnail load procedure." }
+    );
+    %invoke = (
+        code => <<'CODE'
+{
+  gchar *canonical   = gimp_canonicalize_identifier (load_proc);
+  gchar *canon_thumb = gimp_canonicalize_identifier (thumb_proc);
+
+  success = gimp_plug_in_manager_register_thumb_loader (gimp->plug_in_manager,
+                                                        canonical, canon_thumb);
+
+  g_free (canonical);
+  g_free (canon_thumb);
+}
+CODE
+    );
+}
+
+
+@headers = qw("libgimpbase/gimpbase.h"
+              "libgimpconfig/gimpconfig.h"
+              "core/gimp.h"
+              "core/gimp-utils.h"
+              "plug-in/gimppluginmanager-file.h"
+              "plug-in/gimppluginprocedure.h"
+              "file/file-open.h"
+              "file/file-save.h"
+              "file/file-utils.h");
+
+@procs = qw(file_load
+            file_load_layer
+            file_load_layers
+            file_save
+            file_load_thumbnail
+            file_save_thumbnail
+            register_magic_load_handler
+            register_load_handler
+            register_save_handler
+            register_file_handler_mime
+            register_file_handler_uri
+            register_file_handler_raw
+            register_thumbnail_loader);
+
+%exports = (app => [@procs], lib => [@procs[0..3,5..12]]);
+
+$desc = 'File Operations';
+$doc_title = 'gimpfileops';
+$doc_short_desc = 'Image file operations (load, save, etc.)';
+$doc_long_desc = 'Image file operations (load, save, etc.)';
+
+1;
diff --git a/pdb/groups/floating_sel.pdb b/pdb/groups/floating_sel.pdb
new file mode 100644
index 0000000..fc26cc3
--- /dev/null
+++ b/pdb/groups/floating_sel.pdb
@@ -0,0 +1,222 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub floating_sel_remove {
+    $blurb = <<'BLURB';
+Remove the specified floating selection from its associated drawable.
+BLURB
+
+    $help = <<'HELP';
+This procedure removes the floating selection completely, without any side
+effects. The associated drawable is then set to active.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'floating_sel', type => 'layer',
+         desc => 'The floating selection' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_layer_is_floating_sel (floating_sel))
+    {
+      gimp_image_remove_layer (gimp_item_get_image (GIMP_ITEM (floating_sel)),
+                               floating_sel, TRUE, NULL);
+    }
+  else
+    {
+      g_set_error_literal (error, GIMP_PDB_ERROR,
+                           GIMP_PDB_ERROR_INVALID_ARGUMENT,
+                           _("Cannot remove this layer because "
+                             "it is not a floating selection."));
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub floating_sel_anchor {
+    $blurb = <<'BLURB';
+Anchor the specified floating selection to its associated drawable.
+BLURB
+
+    $help = <<'HELP';
+This procedure anchors the floating selection to its associated drawable. This 
+is similar to merging with a merge type of ClipToBottomLayer.  The floating
+selection layer is no longer valid after this operation.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'floating_sel', type => 'layer',
+         desc => 'The floating selection' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_layer_is_floating_sel (floating_sel))
+    {
+      floating_sel_anchor (floating_sel);
+    }
+  else
+    {
+      g_set_error_literal (error, GIMP_PDB_ERROR,
+                           GIMP_PDB_ERROR_INVALID_ARGUMENT,
+                           _("Cannot anchor this layer because "
+                             "it is not a floating selection."));
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub floating_sel_to_layer {
+    $blurb = 'Transforms the specified floating selection into a layer.';
+
+    $help = <<'HELP';
+This procedure transforms the specified floating selection into a layer with
+the same offsets and extents. The composited image will look precisely the
+same, but the floating selection layer will no longer be clipped to the extents
+of the drawable it was attached to. The floating selection will become the
+active layer. This procedure will not work if the floating selection has a
+different base type from the underlying image. This might be the case if the
+floating selection is above an auxiliary channel or a layer mask.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'floating_sel', type => 'layer',
+         desc => 'The floating selection' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_layer_is_floating_sel (floating_sel))
+    {
+      success = floating_sel_to_layer (floating_sel, error);
+    }
+  else
+    {
+      g_set_error_literal (error, GIMP_PDB_ERROR,
+                           GIMP_PDB_ERROR_INVALID_ARGUMENT,
+                           _("Cannot convert this layer to a normal layer "
+                             "because it is not a floating selection."));
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub floating_sel_attach {
+    $blurb = <<'BLURB';
+Attach the specified layer as floating to the specified drawable.
+BLURB
+
+    $help = <<'HELP';
+This procedure attaches the layer as floating selection to the drawable.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'layer', type => 'layer',
+         desc => 'The layer (is attached as floating selection)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable (where to attach the floating selection)' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    floating_sel_attach (layer, drawable);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub floating_sel_rigor {
+    &std_pdb_deprecated ();
+
+    @inargs = (
+       { name => 'floating_sel', type => 'layer', dead => 1,
+         desc => 'The floating selection' },
+       { name => 'undo', type => 'boolean', dead => 1 }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+}
+CODE
+    );
+}
+
+sub floating_sel_relax {
+    &std_pdb_deprecated ();
+
+    @inargs = (
+       { name => 'floating_sel', type => 'layer', dead => 1,
+         desc => 'The floating selection' },
+       { name => 'undo', type => 'boolean', dead => 1 }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+}
+CODE
+    );
+}
+
+
+@headers = qw("core/gimpimage.h"
+              "core/gimplayer-floating-selection.h"
+              "gimppdberror.h"
+              "gimppdb-utils.h"
+              "gimp-intl.h");
+
+@procs = qw(floating_sel_remove
+            floating_sel_anchor
+            floating_sel_to_layer
+            floating_sel_attach
+            floating_sel_rigor
+            floating_sel_relax);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Floating selections';
+$doc_title = 'gimpfloatingsel';
+$doc_short_desc = 'Functions for removing or attaching floating selections.';
+$doc_long_desc = 'Functions for removing or attaching floating selections.';
+
+1;
diff --git a/pdb/groups/font_select.pdb b/pdb/groups/font_select.pdb
new file mode 100644
index 0000000..5ec44fa
--- /dev/null
+++ b/pdb/groups/font_select.pdb
@@ -0,0 +1,111 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub fonts_popup {
+    $blurb = 'Invokes the Gimp font selection.';
+    $help  = 'This procedure opens the font selection dialog.';
+
+    &neo_pdb_misc('2003');
+
+    @inargs = (
+       { name => 'font_callback', type => 'string', non_empty => 1,
+         desc => 'The callback PDB proc to call when font selection is made' },
+       { name => 'popup_title', type => 'string',
+         desc => 'Title of the font selection dialog' },
+       { name => 'initial_font', type => 'string', null_ok => 1,
+         desc => 'The name of the font to set as the first selected' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp->no_interface ||
+      ! gimp_pdb_lookup_procedure (gimp->pdb, font_callback) ||
+      ! gimp_pdb_dialog_new (gimp, context, progress, gimp->fonts,
+                             popup_title, font_callback, initial_font,
+                             NULL))
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub fonts_close_popup {
+    $blurb = 'Close the font selection dialog.';
+    $help  = 'This procedure closes an opened font selection dialog.';
+
+    &neo_pdb_misc('2003');
+
+    @inargs = (
+       { name => 'font_callback', type => 'string', non_empty => 1,
+         desc => 'The name of the callback registered for this pop-up' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp->no_interface ||
+      ! gimp_pdb_lookup_procedure (gimp->pdb, font_callback) ||
+      ! gimp_pdb_dialog_close (gimp, gimp->fonts, font_callback))
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub fonts_set_popup {
+    $blurb = 'Sets the current font in a font selection dialog.';
+    $help  = $blurb;
+
+    &neo_pdb_misc('2003');
+
+    @inargs = (
+       { name => 'font_callback', type => 'string', non_empty => 1,
+         desc => 'The name of the callback registered for this pop-up' },
+       { name => 'font_name', type => 'string',
+         desc => 'The name of the font to set as selected' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp->no_interface ||
+      ! gimp_pdb_lookup_procedure (gimp->pdb, font_callback) ||
+      ! gimp_pdb_dialog_set (gimp, gimp->fonts, font_callback, font_name,
+                             NULL))
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+@headers = qw("core/gimp.h");
+
+@procs = qw(fonts_popup
+            fonts_close_popup
+            fonts_set_popup);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Font UI';
+$doc_title = 'gimpfontselect';
+$doc_short_desc = 'Functions providing a font selection dialog.';
+$doc_long_desc = 'Functions providing a font selection dialog.';
+
+1;
diff --git a/pdb/groups/fonts.pdb b/pdb/groups/fonts.pdb
new file mode 100644
index 0000000..b3cad71
--- /dev/null
+++ b/pdb/groups/fonts.pdb
@@ -0,0 +1,86 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub fonts_refresh {
+    $blurb = 'Refresh current fonts. This function always succeeds.';
+
+    $help = <<'HELP';
+This procedure retrieves all fonts currently in the user's font path
+and updates the font dialogs accordingly. Depending on the amount
+of fonts on the system, this can take considerable time.
+HELP
+
+    &neo_pdb_misc('2003');
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_fonts_load (gimp, NULL);
+}
+CODE
+    );
+}
+
+sub fonts_get_list {
+    $blurb = 'Retrieve the list of loaded fonts.';
+
+    $help = <<'HELP';
+This procedure returns a list of the fonts that are currently available.
+HELP
+
+    &neo_pdb_misc('2003');
+
+    @inargs = (
+       { name => 'filter', type => 'string', null_ok => 1,
+          desc => 'An optional regular expression used to filter the list' }
+    );
+
+    @outargs = (
+       { name => 'font_list', type => 'stringarray',
+         desc => 'The list of font names',
+         array => { name => 'num_fonts',
+                    desc => 'The number of available fonts' } }
+    );
+
+    %invoke = (
+        headers => [ qw("core/gimpcontainer-filter.h") ],
+       code => <<'CODE'
+{
+  font_list = gimp_container_get_filtered_name_array (gimp->fonts,
+                                                      filter, &num_fonts);
+}
+CODE
+    );
+}
+
+
+@headers = qw("core/gimp.h"
+              "core/gimpcontainer.h"
+              "text/gimp-fonts.h");
+
+@procs = qw(fonts_refresh
+            fonts_get_list);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Fonts';
+$doc_title = 'gimpfonts';
+$doc_short_desc = 'Operations related to fonts.';
+$doc_long_desc = 'Operations related to fonts.';
+
+1;
diff --git a/pdb/groups/gimp.pdb b/pdb/groups/gimp.pdb
new file mode 100644
index 0000000..6efe294
--- /dev/null
+++ b/pdb/groups/gimp.pdb
@@ -0,0 +1,244 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub version {
+    $blurb = 'Returns the host GIMP version.';
+
+    $help = <<'HELP';
+This procedure returns the version number of the currently running GIMP.
+HELP
+
+    &yosh_pdb_misc('1999');
+
+    @outargs = (
+       { name => 'version', type => 'string',
+         desc => 'GIMP version number' }
+    );
+
+    %invoke = (
+       headers => [ qw("libgimpbase/gimpbase.h") ],
+       code    => <<'CODE'
+{
+  version = g_strdup (GIMP_VERSION);
+}
+CODE
+    );
+}
+
+sub getpid {
+    $blurb = 'Returns the PID of the host GIMP process.';
+
+    $help = <<'HELP';
+This procedure returns the process ID of the currently running GIMP.
+HELP
+
+    &mitch_pdb_misc('2005', '2.4');
+
+    @outargs = (
+       { name => 'pid', type => 'int32',
+         desc => 'The PID' }
+    );
+
+    %invoke = (
+       headers => [ qw("core/gimp-utils.h") ],
+       code    => <<'CODE'
+{
+  pid = gimp_get_pid ();
+}
+CODE
+    );
+}
+
+sub quit {
+    $blurb = 'Causes GIMP to exit gracefully.';
+
+    $help = <<'HELP';
+If there are unsaved images in an interactive GIMP session, the user
+will be asked for confirmation. If force is TRUE, the application is
+quit without querying the user to save any dirty images.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'force', type => 'boolean',
+         desc => 'Force GIMP to quit without asking' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_exit (gimp, force);
+}
+CODE
+    );
+}
+
+sub attach_parasite {
+    $blurb = 'Add a global parasite.';
+
+    $help = <<'HELP';
+This procedure attaches a global parasite. It has no return values.
+HELP
+
+    &jay_pdb_misc('1998', '2.8');
+
+    @inargs = (
+       { name => 'parasite', type => 'parasite',
+         desc => 'The parasite to attach' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_parasite_validate (gimp, parasite, error))
+    gimp_parasite_attach (gimp, parasite);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub detach_parasite {
+    $blurb = 'Removes a global parasite.';
+
+    $help = <<'HELP';
+This procedure detaches a global parasite from. It has no return values.
+HELP
+
+    &jay_pdb_misc('1998', '2.8');
+
+    @inargs = (
+       { name => 'name', type => 'string',
+         desc => 'The name of the parasite to detach.' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_parasite_detach (gimp, name);
+}
+CODE
+    );
+}
+
+sub get_parasite {
+    $blurb = 'Look up a global parasite.';
+
+    $help = <<'HELP';
+Finds and returns the global parasite that was previously attached.
+HELP
+
+    &jay_pdb_misc('1998', '2.8');
+
+    @inargs = (
+       { name => 'name', type => 'string',
+         desc => 'The name of the parasite to find' }
+    );
+
+    @outargs = (
+       { name => 'parasite', type => 'parasite',
+         desc => 'The found parasite' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  parasite = gimp_parasite_copy (gimp_parasite_find (gimp, name));
+
+  if (! parasite)
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub get_parasite_list {
+    $blurb = 'List all parasites.';
+    $help  = 'Returns a list of all currently attached global parasites.';
+
+    &marc_pdb_misc('1999', '2.8');
+
+    @outargs = (
+       { name => 'parasites', type => 'stringarray',
+         desc => 'The names of currently attached parasites',
+         array => { desc => 'The number of attached parasites' } }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  parasites = gimp_parasite_list (gimp, &num_parasites);
+}
+CODE
+    );
+}
+sub temp_name {
+    $blurb = 'Generates a unique filename.';
+
+    $help = <<'HELP';
+Generates a unique filename using the temp path supplied in the user's gimprc.
+HELP
+
+    &josh_pdb_misc('1997');
+
+    @inargs = (
+        { name => 'extension', type => 'string',
+         allow_non_utf8 => 1, null_ok => 1,
+          desc => 'The extension the file will have' }
+    );
+
+    @outargs = (
+        { name => 'name', type => 'string',
+          desc => 'The new temp filename' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GFile *file = gimp_get_temp_file (gimp, extension);
+
+  name = g_file_get_path (file);
+
+  g_object_unref (file);
+}
+CODE
+    );
+}
+
+
+@headers = qw("core/gimp.h"
+              "core/gimp-parasites.h");
+
+@procs = qw(version
+            getpid
+            quit
+            attach_parasite detach_parasite
+            get_parasite
+            get_parasite_list
+            temp_name);
+
+%exports = (app => [@procs], lib => [@procs[0..1,3..7]]);
+
+$desc = 'Miscellaneous';
+$doc_title = 'gimp';
+$doc_short_desc = 'Miscellaneous procedures';
+$doc_long_desc = 'Miscellaneous procedures not fitting in any category.';
+
+1;
diff --git a/pdb/groups/gimprc.pdb b/pdb/groups/gimprc.pdb
new file mode 100644
index 0000000..70e339a
--- /dev/null
+++ b/pdb/groups/gimprc.pdb
@@ -0,0 +1,284 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub gimprc_query {
+    $blurb = <<'BLURB';
+Queries the gimprc file parser for information on a specified token.
+BLURB
+
+    $help = <<'HELP';
+This procedure is used to locate additional information contained in the gimprc
+file considered extraneous to the operation of GIMP. Plug-ins that need
+configuration information can expect it will be stored in the user gimprc
+file and can use this procedure to retrieve it. This query procedure will
+return the value associated with the specified token. This corresponds _only_
+to entries with the format: (<token> <value>). The value must be a string.
+Entries not corresponding to this format will cause warnings to be issued on
+gimprc parsing and will not be queryable.
+HELP
+
+    &std_pdb_misc;
+    $date = '1997';
+
+    @inargs = (
+       { name => 'token', type => 'string',
+         desc => 'The token to query for' }
+    );
+
+    @outargs = (
+       { name => 'value', type => 'string',
+         desc => 'The value associated with the queried token' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (strlen (token))
+    {
+      /*  use edit_config because unknown tokens are set there  */
+      value = gimp_rc_query (GIMP_RC (gimp->edit_config), token);
+
+      if (! value)
+        success = FALSE;
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub gimprc_set {
+    $blurb = 'Sets a gimprc token to a value and saves it in the gimprc.';
+
+    $help = <<'HELP';
+This procedure is used to add or change additional information in the gimprc
+file that is considered extraneous to the operation of GIMP. Plug-ins that
+need configuration information can use this function to store it, and
+gimp_gimprc_query() to retrieve it.  This will accept _only_ string values in
+UTF-8 encoding.
+HELP
+
+    &seth_pdb_misc('1999');
+       
+    @inargs = (
+       { name => 'token', type => 'string',
+         desc => 'The token to add or modify' },
+       { name => 'value', type => 'string',
+         desc => 'The value to set the token to' }
+    );
+
+    %invoke = ( 
+       code => <<'CODE'
+{
+  if (strlen (token))
+    {
+      /*  use edit_config because that's the one that gets saved  */
+      gimp_rc_set_unknown_token (GIMP_RC (gimp->edit_config), token, value);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub get_monitor_resolution {
+    $blurb = 'Get the monitor resolution as specified in the Preferences.';
+
+    $help = <<'HELP';
+Returns the resolution of the monitor in pixels/inch. This value
+is taken from the Preferences (or the windowing system if this is set in
+the Preferences) and there's no guarantee for the value to be reasonable.
+HELP
+
+    &std_pdb_misc;
+
+    @outargs = (
+       {  name => 'xres', type => 'float', void_ret => 1,
+          desc => 'X resolution' },
+       {  name => 'yres', type => 'float',
+          desc => 'Y resolution' },       
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  xres = GIMP_DISPLAY_CONFIG (gimp->config)->monitor_xres;
+  yres = GIMP_DISPLAY_CONFIG (gimp->config)->monitor_yres;
+}
+CODE
+    );
+}
+
+sub get_default_comment {
+    $blurb = 'Get the default image comment as specified in the Preferences.';
+    $help  = 'Returns a copy of the default image comment.';
+
+    &std_pdb_misc;
+
+    @outargs = (
+       {  name => 'comment', type => 'string',
+          desc => 'Default image comment' }
+    );
+
+    %invoke = (
+       headers => [ qw("core/gimptemplate.h") ],
+       code => <<'CODE'
+{
+  comment = g_strdup (gimp_template_get_comment (gimp->config->default_image));
+}
+CODE
+    );
+}
+
+sub get_default_unit {
+    $blurb = 'Get the default unit (taken from the user\'s locale).';
+    $help  = 'Returns the default unit\'s integer ID.';
+
+    &std_pdb_misc;
+    $since = '2.4';
+
+    @outargs = (
+       {  name => 'unit_id', type => 'unit',
+          desc => 'Default unit' }
+    );
+
+    %invoke = (
+       headers => [ qw("libgimpbase/gimpbase.h"
+                        "core/gimp-utils.h") ],
+       code => <<'CODE'
+{
+  unit_id = gimp_get_default_unit ();
+}
+CODE
+    );
+}
+
+sub get_theme_dir {
+    $blurb = 'Get the directory of the current GUI theme.';
+    $help  = 'Returns a copy of the current GUI theme dir.';
+
+    &std_pdb_misc;
+
+    @outargs = (
+       {  name => 'theme_dir', type => 'string',
+          desc => 'The GUI theme dir' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GFile *file = gimp_get_theme_dir (gimp);
+
+  if (file)
+    theme_dir = g_file_get_path (file);
+}
+CODE
+    );
+}
+
+sub get_icon_theme_dir {
+    $blurb = 'Get the directory of the current icon theme.';
+    $help  = 'Returns a copy of the current icon theme dir.';
+
+    &mitch_pdb_misc('2015', '2.10');
+
+    @outargs = (
+       {  name => 'icon_theme_dir', type => 'string',
+          desc => 'The icon theme dir' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GFile *file = gimp_get_icon_theme_dir (gimp);
+
+  if (file)
+    icon_theme_dir = g_file_get_path (file);
+}
+CODE
+    );
+}
+
+sub get_color_configuration {
+    $blurb = 'Get a serialized version of the color management configuration.';
+    $help = 'Returns a string that can be deserialized into a GimpColorConfig object representing the 
current color management configuration.';
+
+    &neo_pdb_misc('2005', '2.4');
+
+    @outargs = (
+       {  name => 'config', type => 'string',
+          desc => 'Serialized color management configuration',
+          wrap => 1 }
+    );
+
+    %invoke = (
+       headers => [ qw("libgimpconfig/gimpconfig.h") ],
+       code => <<'CODE'
+{
+  config = gimp_config_serialize_to_string (GIMP_CONFIG (gimp->config->color_management), NULL);
+}
+CODE
+    );
+}
+
+sub get_module_load_inhibit {
+    $blurb = 'Get the list of modules which should not be loaded.';
+    $help  = 'Returns a copy of the list of modules which should not be loaded.';
+
+    &std_pdb_misc;
+
+    @outargs = (
+       {  name => 'load_inhibit', type => 'string',
+          desc => 'The list of modules' }
+    );
+
+    %invoke = (
+       headers => [ qw("libgimpmodule/gimpmodule.h") ],
+       code => <<'CODE'
+{
+  load_inhibit = g_strdup (gimp_module_db_get_load_inhibit (gimp->module_db));
+}
+CODE
+    );
+}
+
+
+@headers = qw(<string.h>
+              "config/gimprc.h"
+              "core/gimp.h");
+
+@procs = qw(gimprc_query
+            gimprc_set 
+            get_default_comment
+            get_default_unit
+            get_monitor_resolution
+            get_theme_dir
+            get_icon_theme_dir
+            get_color_configuration
+            get_module_load_inhibit);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Gimprc procedures';
+$doc_title = 'gimpgimprc';
+$doc_short_desc = 'Interactions with settings from gimprc.';
+$doc_long_desc = 'Interactions with settings from gimprc.';
+
+1;
diff --git a/pdb/groups/gradient.pdb b/pdb/groups/gradient.pdb
new file mode 100644
index 0000000..303c9f5
--- /dev/null
+++ b/pdb/groups/gradient.pdb
@@ -0,0 +1,1466 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+sub gradient_new {
+    $blurb = 'Creates a new gradient';
+    $help  = 'This procedure creates a new, uninitialized gradient';
+
+    &shlomi_pdb_misc('2003', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The requested name of the new gradient' }
+    );
+
+    @outargs = (
+       { name => 'actual_name', type => 'string',
+         desc => 'The actual new gradient name' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpData *data = gimp_data_factory_data_new (gimp->gradient_factory,
+                                               context, name);
+
+  if (data)
+    actual_name = g_strdup (gimp_object_get_name (data));
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub gradient_duplicate {
+    $blurb = 'Duplicates a gradient';
+    $help  = 'This procedure creates an identical gradient by a different name';
+
+    &shlomi_pdb_misc('2003', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The gradient name' }
+    );
+
+    @outargs = (
+       { name => 'copy_name', type => 'string',
+         desc => "The name of the gradient's copy" }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpGradient *gradient = gimp_pdb_get_gradient (gimp, name, GIMP_PDB_DATA_ACCESS_READ, error);
+
+  if (gradient)
+    {
+      GimpGradient *gradient_copy = (GimpGradient *)
+        gimp_data_factory_data_duplicate (gimp->gradient_factory,
+                                          GIMP_DATA (gradient));
+
+      if (gradient_copy)
+        copy_name = g_strdup (gimp_object_get_name (gradient_copy));
+      else
+        success = FALSE;
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub gradient_is_editable {
+    $blurb = 'Tests if gradient can be edited';
+    $help  = 'Returns TRUE if you have permission to change the gradient';
+
+    &bill_pdb_misc('2004', '2.4');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The gradient name' }
+    );
+
+    @outargs = (
+       { name => 'editable', type => 'boolean',
+         desc => 'TRUE if the gradient can be edited' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpGradient *gradient = gimp_pdb_get_gradient (gimp, name, GIMP_PDB_DATA_ACCESS_READ, error);
+
+  if (gradient)
+    editable = gimp_data_is_writable (GIMP_DATA (gradient));
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub gradient_rename {
+    $blurb = 'Rename a gradient';
+    $help  = 'This procedure renames a gradient';
+
+    &shlomi_pdb_misc('2003', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The gradient name' },
+        { name => 'new_name', type => 'string', non_empty => 1,
+          desc => 'The new name of the gradient' }
+    );
+
+    @outargs = (
+       { name => 'actual_name', type => 'string',
+         desc => 'The actual new name of the gradient' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpGradient *gradient = gimp_pdb_get_gradient (gimp, name, GIMP_PDB_DATA_ACCESS_RENAME, error);
+
+  if (gradient)
+    {
+      gimp_object_set_name (GIMP_OBJECT (gradient), new_name);
+      actual_name = g_strdup (gimp_object_get_name (gradient));
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub gradient_delete {
+    $blurb = 'Deletes a gradient';
+    $help  = 'This procedure deletes a gradient';
+
+    &shlomi_pdb_misc('2003', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The gradient name' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpGradient *gradient = gimp_pdb_get_gradient (gimp, name, GIMP_PDB_DATA_ACCESS_READ, error);
+
+  if (gradient && gimp_data_is_deletable (GIMP_DATA (gradient)))
+    success = gimp_data_factory_data_delete (gimp->gradient_factory,
+                                             GIMP_DATA (gradient),
+                                             TRUE, error);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub gradient_get_number_of_segments {
+    $blurb = 'Returns the number of segments of the specified gradient';
+
+    $help = <<'HELP';
+This procedure returns the number of segments of the specified gradient.
+HELP
+
+    $author    = 'Lars-Peter Clausen <lars metafoo de>';
+    $copyright = 'Lars-Peter Clausen';
+    $date      = '2008';
+    $since     = '2.6';
+
+
+    @inargs = (
+        { name => 'name', type => 'string', non_empty => 1,
+          desc => 'The gradient name' }
+    );
+
+    @outargs = (
+        { name => 'num_segments', type => 'int32',
+          init => 0, desc => 'Number of segments' }
+    );
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpGradient        *gradient;
+  GimpGradientSegment *seg;
+
+  gradient = gimp_pdb_get_gradient (gimp, name, GIMP_PDB_DATA_ACCESS_READ, error);
+
+  if (gradient)
+    {
+      for (seg = gradient->segments; seg; seg = seg->next)
+        num_segments++;
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub gradient_get_uniform_samples {
+    $blurb = 'Sample the specified in uniform parts.';
+
+    $help = <<'HELP';
+This procedure samples the active gradient in the
+specified number of uniform parts. It returns a list of floating-point values
+which correspond to the RGBA values for each sample. The minimum number of
+samples to take is 2, in which case the returned colors will correspond to the
+{ 0.0, 1.0 } positions in the gradient. For example, if the number of samples
+is 3, the procedure will return the colors at positions { 0.0, 0.5, 1.0 }.
+HELP
+
+    &federico_pdb_misc('1997', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The gradient name' },
+       { name => 'num_samples', type => '2 <= int32',
+         desc => 'The number of samples to take' },
+       { name => 'reverse', type => 'boolean',
+         desc => 'Use the reverse gradient' }
+    );
+
+    @outargs = (
+        { name => 'color_samples', type => 'floatarray', void_ret => 1,
+         desc => 'Color samples: { R1, G1, B1, A1, ..., Rn, Gn, Bn, An }',
+         array => { desc => 'Length of the color_samples array (4 *
+                             num_samples)' } }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpGradient *gradient = gimp_pdb_get_gradient (gimp, name, GIMP_PDB_DATA_ACCESS_READ, error);
+
+  if (gradient)
+    {
+      GimpGradientSegment *seg   = NULL;
+      gdouble              pos   = 0.0;
+      gdouble              delta = 1.0 / (num_samples - 1);
+      gdouble             *sample;
+
+      num_color_samples = num_samples * 4;
+
+      sample = color_samples = g_new (gdouble, num_color_samples);
+
+      while (num_samples--)
+        {
+          GimpRGB color;
+
+         seg = gimp_gradient_get_color_at (gradient, context, seg,
+                                            pos, reverse, &color);
+
+          *sample++ = color.r;
+          *sample++ = color.g;
+          *sample++ = color.b;
+          *sample++ = color.a;
+
+          pos += delta;
+        }
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub gradient_get_custom_samples {
+    $blurb = 'Sample the specified gradient in custom positions.';
+
+    $help = <<'HELP';
+This procedure samples the active gradient in the specified number of
+points. The procedure will sample the gradient in the specified
+positions from the list. The left endpoint of the gradient corresponds
+to position 0.0, and the right endpoint corresponds to 1.0. The
+procedure returns a list of floating-point values which correspond to
+the RGBA values for each sample.
+HELP
+
+    &federico_pdb_misc('1997', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The gradient name' },
+       { name  => 'positions', type  => 'floatarray',
+         desc  => 'The list of positions to sample along the gradient',
+         array => { name => 'num_samples', type => '1 <= int32',
+                    desc => 'The number of samples to take' } },
+       { name => 'reverse', type => 'boolean',
+         desc => 'Use the reverse gradient' }
+    );
+
+    @outargs = (
+        { name => 'color_samples', type => 'floatarray', void_ret => 1,
+         desc => 'Color samples: { R1, G1, B1, A1, ..., Rn, Gn, Bn, An }',
+         array => { desc => 'Length of the color_samples array (4 *
+                             num_samples)' } }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpGradient *gradient = gimp_pdb_get_gradient (gimp, name, GIMP_PDB_DATA_ACCESS_READ, error);
+
+  if (gradient)
+    {
+      GimpGradientSegment *seg = NULL;
+      gdouble             *sample;
+
+      num_color_samples = num_samples * 4;
+
+      sample = color_samples = g_new (gdouble, num_color_samples);
+
+      while (num_samples--)
+        {
+          GimpRGB color;
+
+          seg = gimp_gradient_get_color_at (gradient, context,
+                                            seg, *positions,
+                                            reverse, &color);
+
+          *sample++ = color.r;
+          *sample++ = color.g;
+          *sample++ = color.b;
+          *sample++ = color.a;
+
+          positions++;
+        }
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub gradient_segment_get_left_color {
+    $blurb = 'Retrieves the left endpoint color of the specified segment';
+
+    $help = <<'HELP';
+This procedure retrieves the left endpoint color of the specified segment of
+the specified gradient.
+HELP
+
+    &shlomi_pdb_misc('2003', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The gradient name' },
+        { name => 'segment', type => '0 <= int32',
+          desc => 'The index of the segment within the gradient' }
+    );
+
+    @outargs = (
+        { name => 'color', type => 'color', void_ret => 1,
+          desc => 'The return color' },
+        { name => 'opacity', type => 'float',
+          desc => 'The opacity of the endpoint' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpGradient        *gradient;
+  GimpGradientSegment *seg;
+
+  gradient = gradient_get (gimp, name, GIMP_PDB_DATA_ACCESS_READ, segment, &seg, error);
+
+  if (seg)
+    {
+      gimp_gradient_segment_get_left_color (gradient, seg, &color);
+      opacity = color.a * 100.0;
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub gradient_segment_get_right_color {
+    $blurb = 'Retrieves the right endpoint color of the specified segment';
+
+    $help = <<'HELP';
+This procedure retrieves the right endpoint color of the specified segment of
+the specified gradient.
+HELP
+
+    &shlomi_pdb_misc('2003', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The gradient name' },
+        { name => 'segment', type => '0 <= int32',
+          desc => 'The index of the segment within the gradient' }
+    );
+
+    @outargs = (
+        { name => 'color', type => 'color', void_ret => 1,
+          desc => 'The return color' },
+        { name => 'opacity', type => 'float',
+          desc => 'The opacity of the endpoint' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpGradient        *gradient;
+  GimpGradientSegment *seg;
+
+  gradient = gradient_get (gimp, name, GIMP_PDB_DATA_ACCESS_READ, segment, &seg, error);
+
+  if (seg)
+    {
+      gimp_gradient_segment_get_right_color (gradient, seg, &color);
+      opacity = color.a * 100.0;
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub gradient_segment_set_left_color {
+    $blurb = 'Sets the left endpoint color of the specified segment';
+
+    $help = <<'HELP';
+This procedure sets the left endpoint color of the specified segment of
+the specified gradient.
+HELP
+
+    &shlomi_pdb_misc('2003', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The gradient name' },
+        { name => 'segment', type => '0 <= int32',
+          desc => 'The index of the segment within the gradient' },
+        { name => 'color', type => 'color',
+          desc => 'The color to set' },
+        { name => 'opacity', type => '0 <= float <= 100.0',
+          desc => 'The opacity to set for the endpoint' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpGradient        *gradient;
+  GimpGradientSegment *seg;
+
+  gradient = gradient_get (gimp, name, GIMP_PDB_DATA_ACCESS_WRITE, segment, &seg, error);
+
+  if (seg)
+    {
+      color.a = opacity / 100.0;
+      gimp_gradient_segment_set_left_color (gradient, seg, &color);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub gradient_segment_set_right_color {
+    $blurb = 'Sets the right endpoint color of the specified segment';
+
+    $help = <<'HELP';
+This procedure sets the right endpoint color of the specified segment of
+the specified gradient.
+HELP
+
+    &shlomi_pdb_misc('2003', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The gradient name' },
+        { name => 'segment', type => '0 <= int32',
+          desc => 'The index of the segment within the gradient' },
+        { name => 'color', type => 'color',
+          desc => 'The color to set' },
+        { name => 'opacity', type => '0 <= float <= 100.0',
+          desc => 'The opacity to set for the endpoint' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpGradient        *gradient;
+  GimpGradientSegment *seg;
+
+  gradient = gradient_get (gimp, name, GIMP_PDB_DATA_ACCESS_WRITE, segment, &seg, error);
+
+  if (seg)
+    {
+      color.a = opacity / 100.0;
+      gimp_gradient_segment_set_right_color (gradient, seg, &color);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub gradient_segment_get_left_pos {
+    $blurb = 'Retrieves the left endpoint position of the specified segment';
+
+    $help = <<'HELP';
+This procedure retrieves the left endpoint position of the specified segment of
+the specified gradient.
+HELP
+
+    &shlomi_pdb_misc('2003', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The gradient name' },
+        { name => 'segment', type => '0 <= int32',
+          desc => 'The index of the segment within the gradient' }
+    );
+
+    @outargs = (
+        { name => 'pos', type => 'float', void_ret => 1,
+          desc => 'The return position' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpGradient        *gradient;
+  GimpGradientSegment *seg;
+
+  gradient = gradient_get (gimp, name, GIMP_PDB_DATA_ACCESS_READ, segment, &seg, error);
+
+  if (seg)
+    {
+      pos = gimp_gradient_segment_get_left_pos (gradient, seg);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub gradient_segment_get_right_pos {
+    $blurb = 'Retrieves the right endpoint position of the specified segment';
+
+    $help = <<'HELP';
+This procedure retrieves the right endpoint position of the specified segment of
+the specified gradient.
+HELP
+
+    &shlomi_pdb_misc('2003', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The gradient name' },
+        { name => 'segment', type => '0 <= int32',
+          desc => 'The index of the segment within the gradient' }
+    );
+
+    @outargs = (
+        { name => 'pos', type => 'float', void_ret => 1,
+          desc => 'The return position' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpGradient        *gradient;
+  GimpGradientSegment *seg;
+
+  gradient = gradient_get (gimp, name, GIMP_PDB_DATA_ACCESS_READ, segment, &seg, error);
+
+  if (seg)
+    {
+      pos = gimp_gradient_segment_get_right_pos (gradient, seg);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub gradient_segment_get_middle_pos {
+    $blurb = 'Retrieves the middle point position of the specified segment';
+
+    $help = <<'HELP';
+This procedure retrieves the middle point position of the specified segment of
+the specified gradient.
+HELP
+
+    &shlomi_pdb_misc('2003', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The gradient name' },
+        { name => 'segment', type => '0 <= int32',
+          desc => 'The index of the segment within the gradient' }
+    );
+
+    @outargs = (
+        { name => 'pos', type => 'float', void_ret => 1,
+          desc => 'The return position' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpGradient        *gradient;
+  GimpGradientSegment *seg;
+
+  gradient = gradient_get (gimp, name, GIMP_PDB_DATA_ACCESS_READ, segment, &seg, error);
+
+  if (seg)
+    {
+      pos = gimp_gradient_segment_get_middle_pos (gradient, seg);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub gradient_segment_set_left_pos {
+    $blurb = 'Sets the left endpoint position of the specified segment';
+
+    $help = <<'HELP';
+This procedure sets the left endpoint position of the specified
+segment of the specified gradient. The final position will be between
+the position of the middle point to the left to the middle point of
+the current segment.
+
+This procedure returns the final position.
+HELP
+
+    &shlomi_pdb_misc('2003', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The gradient name' },
+        { name => 'segment', type => '0 <= int32',
+          desc => 'The index of the segment within the gradient' },
+        { name => 'pos', type => '0.0 <= float <= 1.0',
+          desc => 'The position to set the guidepoint to' }
+    );
+
+    @outargs = (
+        { name => 'final_pos', type => 'float', void_ret => 1,
+          desc => 'The return position' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpGradient        *gradient;
+  GimpGradientSegment *seg;
+
+  gradient = gradient_get (gimp, name, GIMP_PDB_DATA_ACCESS_WRITE, segment, &seg, error);
+
+  if (seg)
+    {
+      final_pos = gimp_gradient_segment_set_left_pos (gradient, seg, pos);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub gradient_segment_set_right_pos {
+    $blurb = 'Sets the right endpoint position of the specified segment';
+
+    $help = <<'HELP';
+This procedure sets the right endpoint position of the specified
+segment of the specified gradient. The final position will be between
+the position of the middle point of the current segment and the middle
+point of the segment to the right.
+
+This procedure returns the final position.
+HELP
+
+    &shlomi_pdb_misc('2003', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The gradient name' },
+        { name => 'segment', type => '0 <= int32',
+          desc => 'The index of the segment within the gradient' },
+        { name => 'pos', type => '0.0 <= float <= 1.0',
+          desc => 'The position to set the guidepoint to' }
+    );
+
+    @outargs = (
+        { name => 'final_pos', type => 'float', void_ret => 1,
+          desc => 'The return position' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpGradient        *gradient;
+  GimpGradientSegment *seg;
+
+  gradient = gradient_get (gimp, name, GIMP_PDB_DATA_ACCESS_WRITE, segment, &seg, error);
+
+  if (seg)
+    {
+      final_pos =
+        gimp_gradient_segment_set_right_pos (gradient, seg, pos);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub gradient_segment_set_middle_pos {
+    $blurb = 'Sets the middle point position of the specified segment';
+
+    $help = <<'HELP';
+This procedure sets the middle point position of the specified segment
+of the specified gradient. The final position will be between the two
+endpoints of the segment.
+
+This procedure returns the final position.
+HELP
+
+    &shlomi_pdb_misc('2003', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The gradient name' },
+        { name => 'segment', type => '0 <= int32',
+          desc => 'The index of the segment within the gradient' },
+        { name => 'pos', type => '0.0 <= float <= 1.0',
+          desc => 'The position to set the guidepoint to' }
+    );
+
+    @outargs = (
+        { name => 'final_pos', type => 'float', void_ret => 1,
+          desc => 'The return position' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpGradient        *gradient;
+  GimpGradientSegment *seg;
+
+  gradient = gradient_get (gimp, name, GIMP_PDB_DATA_ACCESS_WRITE, segment, &seg, error);
+
+  if (seg)
+    {
+      final_pos =
+        gimp_gradient_segment_set_middle_pos (gradient, seg, pos);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub gradient_segment_get_blending_function {
+    $blurb = "Retrieves the gradient segment's blending function";
+
+    $help = <<'HELP';
+This procedure retrieves the blending function of the segment at the
+specified gradient name and segment index.
+HELP
+
+    &shlomi_pdb_misc('2003', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The gradient name' },
+        { name => 'segment', type => '0 <= int32',
+          desc => 'The index of the segment within the gradient' }
+    );
+
+    @outargs = (
+        { name => 'blend_func', type => 'enum GimpGradientSegmentType',
+          void_ret => 1,
+         desc => 'The blending function of the segment' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpGradient        *gradient;
+  GimpGradientSegment *seg;
+
+  gradient = gradient_get (gimp, name, GIMP_PDB_DATA_ACCESS_READ, segment, &seg, error);
+
+  if (seg)
+    {
+      blend_func = gimp_gradient_segment_get_blending_function (gradient, seg);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub gradient_segment_get_coloring_type {
+    $blurb = "Retrieves the gradient segment's coloring type";
+
+    $help = <<'HELP';
+This procedure retrieves the coloring type of the segment at the
+specified gradient name and segment index.
+HELP
+
+    &shlomi_pdb_misc('2003', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The gradient name' },
+        { name => 'segment', type => '0 <= int32',
+          desc => 'The index of the segment within the gradient' }
+    );
+
+    @outargs = (
+        { name => 'coloring_type', type => 'enum GimpGradientSegmentColor',
+          void_ret => 1,
+         desc => 'The coloring type of the segment' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpGradient        *gradient;
+  GimpGradientSegment *seg;
+
+  gradient = gradient_get (gimp, name, GIMP_PDB_DATA_ACCESS_READ, segment, &seg, error);
+
+  if (seg)
+    {
+      coloring_type = gimp_gradient_segment_get_coloring_type (gradient, seg);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub gradient_segment_range_set_blending_function {
+    $blurb = 'Change the blending function of a segments range';
+
+    $help = <<'HELP';
+This function changes the blending function of a segment range to the
+specified blending function.
+HELP
+
+    &shlomi_pdb_misc('2003', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The gradient name' },
+        { name => 'start_segment', type => '0 <= int32',
+          desc => 'The index of the first segment to operate on' },
+        { name => 'end_segment', type => 'int32',
+          desc => 'The index of the last segment to operate on. If negative,
+                   the selection will extend to the end of the string.' },
+        { name => 'blending_function', type => 'enum GimpGradientSegmentType',
+          desc => 'The blending function' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpGradient        *gradient;
+  GimpGradientSegment *start_seg;
+  GimpGradientSegment *end_seg;
+
+  gradient = gradient_get_range (gimp, name, start_segment, end_segment,
+                                 &start_seg, &end_seg, error);
+
+  if (start_seg)
+    {
+      gimp_gradient_segment_range_set_blending_function (gradient,
+                                                         start_seg, end_seg,
+                                                         blending_function);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub gradient_segment_range_set_coloring_type {
+    $blurb = 'Change the coloring type of a segments range';
+
+    $help = <<'HELP';
+This function changes the coloring type of a segment range to the
+specified coloring type.
+HELP
+
+    &shlomi_pdb_misc('2003', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The gradient name' },
+        { name => 'start_segment', type => '0 <= int32',
+          desc => 'The index of the first segment to operate on' },
+        { name => 'end_segment', type => 'int32',
+          desc => 'The index of the last segment to operate on. If negative,
+                   the selection will extend to the end of the string.' },
+        { name => 'coloring_type', type => 'enum GimpGradientSegmentColor',
+          desc => 'The coloring type' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpGradient        *gradient;
+  GimpGradientSegment *start_seg;
+  GimpGradientSegment *end_seg;
+
+  gradient = gradient_get_range (gimp, name, start_segment, end_segment,
+                                 &start_seg, &end_seg, error);
+
+  if (start_seg)
+    {
+      gimp_gradient_segment_range_set_coloring_type (gradient,
+                                                     start_seg, end_seg,
+                                                     coloring_type);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub gradient_segment_range_flip {
+    $blurb = 'Flip the segment range';
+    $help  = 'This function flips a segment range.';
+
+    &shlomi_pdb_misc('2003', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The gradient name' },
+        { name => 'start_segment', type => '0 <= int32',
+          desc => 'The index of the first segment to operate on' },
+        { name => 'end_segment', type => 'int32',
+          desc => 'The index of the last segment to operate on. If negative,
+                   the selection will extend to the end of the string.' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpGradient        *gradient;
+  GimpGradientSegment *start_seg;
+  GimpGradientSegment *end_seg;
+
+  gradient = gradient_get_range (gimp, name, start_segment, end_segment,
+                                 &start_seg, &end_seg, error);
+
+  if (start_seg)
+    {
+      gimp_gradient_segment_range_flip (gradient,
+                                        start_seg, end_seg,
+                                        NULL, NULL);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub gradient_segment_range_replicate {
+    $blurb = 'Replicate the segment range';
+
+    $help = <<'HELP';
+This function replicates a segment range a given number of times. Instead
+of the original segment range, several smaller scaled copies of it
+will appear in equal widths.
+HELP
+
+    &shlomi_pdb_misc('2003', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The gradient name' },
+        { name => 'start_segment', type => '0 <= int32',
+          desc => 'The index of the first segment to operate on' },
+        { name => 'end_segment', type => 'int32',
+          desc => 'The index of the last segment to operate on. If negative,
+                   the selection will extend to the end of the string.' },
+        { name => 'replicate_times', type => '2 <= int32 <= 20',
+          desc => 'The number of times to replicate' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpGradient        *gradient;
+  GimpGradientSegment *start_seg;
+  GimpGradientSegment *end_seg;
+
+  gradient = gradient_get_range (gimp, name, start_segment, end_segment,
+                                 &start_seg, &end_seg, error);
+
+  if (start_seg && gimp_data_is_writable (GIMP_DATA (gradient)))
+    {
+      gimp_gradient_segment_range_replicate (gradient,
+                                             start_seg, end_seg,
+                                             replicate_times,
+                                             NULL, NULL);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub gradient_segment_range_split_midpoint {
+    $blurb = 'Splits each segment in the segment range at midpoint';
+
+    $help = <<'HELP';
+This function splits each segment in the segment range at its midpoint.
+HELP
+
+    &shlomi_pdb_misc('2003', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The gradient name' },
+        { name => 'start_segment', type => '0 <= int32',
+          desc => 'The index of the first segment to operate on' },
+        { name => 'end_segment', type => 'int32',
+          desc => 'The index of the last segment to operate on. If negative,
+                   the selection will extend to the end of the string.' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpGradient        *gradient;
+  GimpGradientSegment *start_seg;
+  GimpGradientSegment *end_seg;
+
+  gradient = gradient_get_range (gimp, name, start_segment, end_segment,
+                                 &start_seg, &end_seg, error);
+
+  if (start_seg)
+    {
+      gimp_gradient_segment_range_split_midpoint (gradient, context,
+                                                  start_seg, end_seg,
+                                                  NULL, NULL);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub gradient_segment_range_split_uniform {
+    $blurb = 'Splits each segment in the segment range uniformly';
+
+    $help = <<'HELP';
+This function splits each segment in the segment range uniformly according
+to the number of times specified by the parameter.
+HELP
+
+    &shlomi_pdb_misc('2003', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The gradient name' },
+        { name => 'start_segment', type => '0 <= int32',
+          desc => 'The index of the first segment to operate on' },
+        { name => 'end_segment', type => 'int32',
+          desc => 'The index of the last segment to operate on. If negative,
+                   the selection will extend to the end of the string.' },
+        { name => 'split_parts', type => '2 <= int32 <= 1024',
+          desc => 'The number of uniform divisions to split each segment to' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpGradient        *gradient;
+  GimpGradientSegment *start_seg;
+  GimpGradientSegment *end_seg;
+
+  gradient = gradient_get_range (gimp, name, start_segment, end_segment,
+                                 &start_seg, &end_seg, error);
+
+  if (start_seg)
+    {
+      gimp_gradient_segment_range_split_uniform (gradient, context,
+                                                 start_seg, end_seg,
+                                                 split_parts,
+                                                 NULL, NULL);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub gradient_segment_range_delete {
+    $blurb = 'Delete the segment range';
+    $help  = 'This function deletes a segment range.';
+
+    &shlomi_pdb_misc('2003', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The gradient name' },
+        { name => 'start_segment', type => '0 <= int32',
+          desc => 'The index of the first segment to operate on' },
+        { name => 'end_segment', type => 'int32',
+          desc => 'The index of the last segment to operate on. If negative,
+                   the selection will extend to the end of the string.' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpGradient        *gradient;
+  GimpGradientSegment *start_seg;
+  GimpGradientSegment *end_seg;
+
+  gradient = gradient_get_range (gimp, name, start_segment, end_segment,
+                                 &start_seg, &end_seg, error);
+
+  if (start_seg)
+    {
+      gimp_gradient_segment_range_delete (gradient,
+                                          start_seg, end_seg,
+                                          NULL, NULL);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub gradient_segment_range_redistribute_handles {
+    $blurb = "Uniformly redistribute the segment range's handles";
+
+    $help = <<'HELP';
+This function redistributes the handles of the specified segment range of the
+specified gradient, so they'll be evenly spaced.
+HELP
+
+    &shlomi_pdb_misc('2003', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The gradient name' },
+        { name => 'start_segment', type => '0 <= int32',
+          desc => 'The index of the first segment to operate on' },
+        { name => 'end_segment', type => 'int32',
+          desc => 'The index of the last segment to operate on. If negative,
+                   the selection will extend to the end of the string.' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpGradient        *gradient;
+  GimpGradientSegment *start_seg;
+  GimpGradientSegment *end_seg;
+
+  gradient = gradient_get_range (gimp, name, start_segment, end_segment,
+                                 &start_seg, &end_seg, error);
+
+  if (start_seg)
+    {
+      gimp_gradient_segment_range_redistribute_handles (gradient,
+                                                        start_seg, end_seg);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub gradient_segment_range_blend_colors {
+    $blurb = 'Blend the colors of the segment range.';
+
+    $help = <<'HELP';
+This function blends the colors (but not the opacity) of the segments'
+range of the gradient. Using it, the colors' transition will be uniform
+across the range.
+HELP
+
+    &shlomi_pdb_misc('2003', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The gradient name' },
+        { name => 'start_segment', type => '0 <= int32',
+          desc => 'The index of the first segment to operate on' },
+        { name => 'end_segment', type => 'int32',
+          desc => 'The index of the last segment to operate on. If negative,
+                   the selection will extend to the end of the string.' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpGradient        *gradient;
+  GimpGradientSegment *start_seg;
+  GimpGradientSegment *end_seg;
+
+  gradient = gradient_get_range (gimp, name, start_segment, end_segment,
+                                 &start_seg, &end_seg, error);
+
+  if (start_seg)
+    {
+      if (!end_seg)
+        end_seg = gimp_gradient_segment_get_last (start_seg);
+
+      gimp_gradient_segment_range_blend (gradient,
+                                         start_seg, end_seg,
+                                         &start_seg->left_color,
+                                         &end_seg->right_color,
+                                         TRUE, FALSE);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub gradient_segment_range_blend_opacity {
+    $blurb = 'Blend the opacity of the segment range.';
+
+    $help = <<'HELP';
+This function blends the opacity (but not the colors) of the segments'
+range of the gradient. Using it, the opacity's transition will be uniform
+across the range.
+HELP
+
+    &shlomi_pdb_misc('2003', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The gradient name' },
+        { name => 'start_segment', type => '0 <= int32',
+          desc => 'The index of the first segment to operate on' },
+        { name => 'end_segment', type => 'int32',
+          desc => 'The index of the last segment to operate on. If negative,
+                   the selection will extend to the end of the string.' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpGradient        *gradient;
+  GimpGradientSegment *start_seg;
+  GimpGradientSegment *end_seg;
+
+  gradient = gradient_get_range (gimp, name, start_segment, end_segment,
+                                 &start_seg, &end_seg, error);
+
+  if (start_seg)
+    {
+      if (!end_seg)
+        end_seg = gimp_gradient_segment_get_last (start_seg);
+
+      gimp_gradient_segment_range_blend (gradient,
+                                         start_seg, end_seg,
+                                         &start_seg->left_color,
+                                         &end_seg->right_color,
+                                         FALSE, TRUE);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub gradient_segment_range_move {
+    $blurb = 'Move the position of an entire segment range by a delta.';
+
+    $help = <<'HELP';
+This function moves the position of an entire segment range by a delta. The
+actual delta (which is returned) will be limited by the control points of the
+neighboring segments.
+HELP
+
+    &shlomi_pdb_misc('2003', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The gradient name' },
+        { name => 'start_segment', type => '0 <= int32',
+          desc => 'The index of the first segment to operate on' },
+        { name => 'end_segment', type => 'int32',
+          desc => 'The index of the last segment to operate on. If negative,
+                   the selection will extend to the end of the string.' },
+        { name => 'delta', type => '-1.0 <= float <= 1.0',
+          desc => 'The delta to move the segment range' },
+        { name => 'control_compress', type => 'boolean',
+          desc => 'Whether or not to compress the neighboring segments' }
+    );
+
+    @outargs = (
+        { name => 'final_delta', type => 'float',
+          desc => 'The final delta by which the range moved' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpGradient        *gradient;
+  GimpGradientSegment *start_seg;
+  GimpGradientSegment *end_seg;
+
+  gradient = gradient_get_range (gimp, name, start_segment, end_segment,
+                                 &start_seg, &end_seg, error);
+
+  if (start_seg)
+    {
+      final_delta = gimp_gradient_segment_range_move (gradient,
+                                                      start_seg, end_seg,
+                                                      delta,
+                                                      control_compress);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+$extra{app}->{code} = <<'CODE';
+static GimpGradient *
+gradient_get (Gimp                 *gimp,
+              const gchar          *name,
+              GimpPDBDataAccess     access,
+              gint                  segment,
+              GimpGradientSegment **seg,
+              GError              **error)
+{
+  GimpGradient *gradient = gimp_pdb_get_gradient (gimp, name, access, error);
+
+  *seg = NULL;
+
+  if (gradient)
+    *seg = gimp_gradient_segment_get_nth (gradient->segments, segment);
+
+  return gradient;
+}
+
+static GimpGradient *
+gradient_get_range (Gimp                 *gimp,
+                    const gchar          *name,
+                    gint                  start_segment,
+                    gint                  end_segment,
+                    GimpGradientSegment **start_seg,
+                    GimpGradientSegment **end_seg,
+                    GError              **error)
+{
+  GimpGradient *gradient = gimp_pdb_get_gradient (gimp, name, GIMP_PDB_DATA_ACCESS_WRITE, error);
+
+  *start_seg = NULL;
+  *end_seg   = NULL;
+
+  if (end_segment >= 0 && end_segment < start_segment)
+    return NULL;
+
+  if (gradient)
+    {
+      *start_seg = gimp_gradient_segment_get_nth (gradient->segments,
+                                                  start_segment);
+
+      if (*start_seg && end_segment >= 0)
+        *end_seg = gimp_gradient_segment_get_nth (*start_seg,
+                                                  end_segment -
+                                                  start_segment);
+    }
+
+  return gradient;
+}
+CODE
+
+
+@headers = qw(<string.h>
+              "core/gimp.h"
+              "core/gimpcontext.h"
+              "core/gimpgradient.h"
+              "core/gimpdatafactory.h"
+              "gimppdb-utils.h");
+
+@procs = qw(gradient_new
+            gradient_duplicate
+            gradient_is_editable
+            gradient_rename
+            gradient_delete
+            gradient_get_number_of_segments
+            gradient_get_uniform_samples gradient_get_custom_samples
+            gradient_segment_get_left_color gradient_segment_set_left_color
+            gradient_segment_get_right_color gradient_segment_set_right_color
+            gradient_segment_get_left_pos gradient_segment_set_left_pos
+            gradient_segment_get_middle_pos gradient_segment_set_middle_pos
+            gradient_segment_get_right_pos gradient_segment_set_right_pos
+            gradient_segment_get_blending_function
+            gradient_segment_get_coloring_type
+            gradient_segment_range_set_blending_function 
+            gradient_segment_range_set_coloring_type
+            gradient_segment_range_flip
+            gradient_segment_range_replicate
+            gradient_segment_range_split_midpoint
+            gradient_segment_range_split_uniform
+            gradient_segment_range_delete
+            gradient_segment_range_redistribute_handles
+            gradient_segment_range_blend_colors
+            gradient_segment_range_blend_opacity
+            gradient_segment_range_move);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Gradient';
+$doc_title = 'gimpgradient';
+$doc_short_desc = 'Functions operating on a single gradient.';
+$doc_long_desc = 'Functions operating on a single gradient.';
+
+1;
diff --git a/pdb/groups/gradient_select.pdb b/pdb/groups/gradient_select.pdb
new file mode 100644
index 0000000..b7ffb8d
--- /dev/null
+++ b/pdb/groups/gradient_select.pdb
@@ -0,0 +1,123 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub gradients_popup {
+    $blurb = 'Invokes the Gimp gradients selection.';
+    $help  = 'This procedure opens the gradient selection dialog.';
+
+    &andy_pdb_misc('1998');
+
+    @inargs = (
+       { name => 'gradient_callback', type => 'string', non_empty => 1,
+         desc => 'The callback PDB proc to call when gradient selection is
+                  made' },
+       { name => 'popup_title', type => 'string',
+         desc => 'Title of the gradient selection dialog' },
+       { name => 'initial_gradient', type => 'string', null_ok => 1,
+         desc => 'The name of the gradient to set as the first selected' },
+       { name => 'sample_size', type => '1 <= int32 <= 10000', no_validate => 1,
+         desc => 'Size of the sample to return when the gradient is changed' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (sample_size < 1 || sample_size > 10000)
+    sample_size = GIMP_GRADIENT_DEFAULT_SAMPLE_SIZE;
+
+  if (gimp->no_interface ||
+      ! gimp_pdb_lookup_procedure (gimp->pdb, gradient_callback) ||
+      ! gimp_pdb_dialog_new (gimp, context, progress,
+                             gimp_data_factory_get_container (gimp->gradient_factory),
+                             popup_title, gradient_callback, initial_gradient,
+                             "sample-size", sample_size,
+                             NULL))
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub gradients_close_popup {
+    $blurb = 'Close the gradient selection dialog.';
+    $help  = 'This procedure closes an opened gradient selection dialog.';
+
+    &andy_pdb_misc('1998');
+
+    @inargs = (
+       { name => 'gradient_callback', type => 'string', non_empty => 1,
+         desc => 'The name of the callback registered for this pop-up' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp->no_interface ||
+      ! gimp_pdb_lookup_procedure (gimp->pdb, gradient_callback) ||
+      ! gimp_pdb_dialog_close (gimp, gimp_data_factory_get_container (gimp->gradient_factory),
+                               gradient_callback))
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub gradients_set_popup {
+    $blurb = 'Sets the current gradient in a gradient selection dialog.';
+    $help  = $blurb;
+
+    &andy_pdb_misc('1998');
+
+    @inargs = (
+       { name => 'gradient_callback', type => 'string', non_empty => 1,
+         desc => 'The name of the callback registered for this pop-up' },
+       { name => 'gradient_name', type => 'string',
+         desc => 'The name of the gradient to set as selected' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp->no_interface ||
+      ! gimp_pdb_lookup_procedure (gimp->pdb, gradient_callback) ||
+      ! gimp_pdb_dialog_set (gimp, gimp_data_factory_get_container (gimp->gradient_factory),
+                             gradient_callback, gradient_name,
+                             NULL))
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+@headers = qw("core/gimp.h"
+              "core/gimpdatafactory.h"
+              "core/gimpgradient.h");
+
+@procs = qw(gradients_popup
+            gradients_close_popup
+            gradients_set_popup);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Gradient UI';
+$doc_title = 'gimpgradientselect';
+$doc_short_desc = 'Functions providing a gradient selection dialog.';
+$doc_long_desc = 'Functions providing a gradient selection dialog.';
+
+1;
diff --git a/pdb/groups/gradients.pdb b/pdb/groups/gradients.pdb
new file mode 100644
index 0000000..336c412
--- /dev/null
+++ b/pdb/groups/gradients.pdb
@@ -0,0 +1,268 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub gradients_refresh {
+    $blurb = 'Refresh current gradients. This function always succeeds.';
+
+    $help = <<'HELP';
+This procedure retrieves all gradients currently in the user's gradient path
+and updates the gradient dialogs accordingly.
+HELP
+
+    &mitch_pdb_misc('2002');
+
+    %invoke = (
+        code => <<'CODE'
+{
+  gimp_data_factory_data_refresh (gimp->gradient_factory, context);
+}
+CODE
+    );
+}
+
+sub gradients_get_list {
+    $blurb = 'Retrieve the list of loaded gradients.';
+
+    $help = <<'HELP';
+This procedure returns a list of the gradients that are currently loaded.
+You can later use the gimp_context_set_gradient() function to
+set the active gradient.
+HELP
+
+    &federico_pdb_misc('1997');
+
+    @inargs = (
+       { name => 'filter', type => 'string', null_ok => 1,
+          desc => 'An optional regular expression used to filter the list' }
+    );
+
+    @outargs = (
+       { name => 'gradient_list', type => 'stringarray',
+         desc => 'The list of gradient names',
+         array => { name => 'num_gradients',
+                    desc => 'The number of loaded gradients' } }
+    );
+
+    %invoke = (
+        headers => [ qw("core/gimpcontainer-filter.h") ],
+       code => <<'CODE'
+{
+  gradient_list = gimp_container_get_filtered_name_array (gimp_data_factory_get_container 
(gimp->gradient_factory),
+                                                          filter, &num_gradients);
+}
+CODE
+    );
+}
+
+sub gradients_sample_uniform {
+    &std_pdb_deprecated ('gimp-gradient-get-uniform-samples');
+
+    @inargs = (
+       { name => 'num_samples', type => '2 <= int32',
+         desc => 'The number of samples to take' },
+       { name => 'reverse', type => 'boolean',
+         desc => 'Use the reverse gradient' }
+    );
+
+    @outargs = (
+        { name => 'color_samples', type => 'floatarray',
+         desc => 'Color samples: { R1, G1, B1, A1, ..., Rn, Gn, Bn, An }',
+         array => { name => 'array_length', no_lib => 1,
+                    desc => 'Length of the color_samples array (4 *
+                             num_samples)' } }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpGradient        *gradient;
+  GimpGradientSegment *seg = NULL;
+  gdouble              pos, delta;
+  GimpRGB              color;
+  gdouble             *pv;
+
+  pos   = 0.0;
+  delta = 1.0 / (num_samples - 1);
+
+  array_length = num_samples * 4;
+
+  pv = color_samples = g_new (gdouble, array_length);
+
+  gradient = gimp_context_get_gradient (context);
+
+  while (num_samples--)
+    {
+      seg = gimp_gradient_get_color_at (gradient, context, seg,
+                                        pos, reverse, &color);
+
+      *pv++ = color.r;
+      *pv++ = color.g;
+      *pv++ = color.b;
+      *pv++ = color.a;
+
+      pos += delta;
+    }
+}
+CODE
+    );
+}
+
+sub gradients_sample_custom {
+    &std_pdb_deprecated ('gimp-gradient-get-custom-samples');
+
+    @inargs = (
+       { name  => 'positions', type  => 'floatarray',
+         desc  => 'The list of positions to sample along the gradient',
+         array => { name => 'num_samples',
+                    desc => 'The number of samples to take' } },
+       { name => 'reverse', type => 'boolean',
+         desc => 'Use the reverse gradient' }
+    );
+
+    @outargs = (
+        { name => 'color_samples', type => 'floatarray',
+         desc => 'Color samples: { R1, G1, B1, A1, ..., Rn, Gn, Bn, An }',
+         array => { name => 'array_length', no_lib => 1,
+                    desc => 'Length of the color_samples array (4 *
+                             num_samples)' } }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpGradient        *gradient;
+  GimpGradientSegment *seg = NULL;
+  GimpRGB              color;
+  gdouble             *pv;
+
+  array_length = num_samples * 4;
+
+  pv = color_samples = g_new (gdouble, array_length);
+
+  gradient = gimp_context_get_gradient (context);
+
+  while (num_samples--)
+    {
+      seg = gimp_gradient_get_color_at (gradient, context, seg,
+                                        *positions, reverse, &color);
+
+      *pv++ = color.r;
+      *pv++ = color.g;
+      *pv++ = color.b;
+      *pv++ = color.a;
+
+      positions++;
+    }
+}
+CODE
+    );
+}
+
+sub gradients_get_gradient_data {
+    &std_pdb_deprecated ('gimp-gradient-get-uniform-samples');
+
+    @inargs = (
+       { name => 'name', type => 'string', null_ok => 1,
+         desc => 'The gradient name ("" means current active gradient)' },
+       { name => 'sample_size', type => '1 <= int32 <= 10000',
+         no_validate => 1,
+         desc => 'Size of the sample to return when the gradient is changed' },
+       { name => 'reverse', type => 'boolean',
+         desc => 'Use the reverse gradient' }
+    );
+
+    @outargs = (
+       { name => 'actual_name', type => 'string',
+         desc => 'The gradient name' },
+       { name => 'grad_data', type => 'floatarray',
+         desc => 'The gradient sample data',
+         array => { name => 'width',
+                    desc => 'The gradient sample width (r,g,b,a)' } }
+    );
+
+    %invoke = (
+       code => <<"CODE"
+{
+  GimpGradient *gradient;
+
+  if (sample_size < 1 || sample_size > 10000)
+    sample_size = GIMP_GRADIENT_DEFAULT_SAMPLE_SIZE;
+
+  if (name && strlen (name))
+    gradient = gimp_pdb_get_gradient (gimp, name, FALSE, error);
+  else
+    gradient = gimp_context_get_gradient (context);
+
+  if (gradient)
+    {
+      GimpGradientSegment *seg = NULL;
+      gdouble             *pv;
+      gdouble              pos, delta;
+      GimpRGB              color;
+
+      pos   = 0.0;
+      delta = 1.0 / (sample_size - 1);
+
+      actual_name = g_strdup (gimp_object_get_name (gradient));
+      grad_data   = g_new (gdouble, sample_size * 4);
+      width       = sample_size * 4;
+
+      pv = grad_data;
+
+      while (sample_size--)
+       {
+         seg = gimp_gradient_get_color_at (gradient, context, seg,
+                                            pos, reverse, &color);
+
+         *pv++ = color.r;
+         *pv++ = color.g;
+         *pv++ = color.b;
+         *pv++ = color.a;
+
+         pos += delta;
+       }
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+@headers = qw(<string.h>
+              "core/gimp.h"
+              "core/gimpcontext.h"
+              "core/gimpdatafactory.h"
+              "core/gimpgradient.h"
+              "gimppdb-utils.h");
+
+@procs = qw(gradients_refresh
+            gradients_get_list
+           gradients_sample_uniform
+            gradients_sample_custom
+            gradients_get_gradient_data);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Gradients';
+$doc_title = 'gimpgradients';
+$doc_short_desc = 'Operations related to gradients.';
+$doc_long_desc = 'Operations related to gradients.';
+
+1;
diff --git a/pdb/groups/help.pdb b/pdb/groups/help.pdb
new file mode 100644
index 0000000..4eb17c6
--- /dev/null
+++ b/pdb/groups/help.pdb
@@ -0,0 +1,72 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Sven Neumann <sven gimp org>
+
+sub help {
+    $blurb = "Load a help page.";
+
+    $help = <<HELP;
+This procedure loads the specified help page into the helpbrowser or
+what ever is configured as help viewer. The help page is identified by
+its domain and ID: if help_domain is NULL, we use the help_domain
+which was registered using the gimp_plugin_help_register() procedure. If
+help_domain is NULL and no help domain was registered, the help domain
+of the main GIMP installation is used.
+HELP
+
+    &mitch_pdb_misc('2000');
+
+    @inargs = (
+       { name => 'help_domain', type => 'string', null_ok => 1,
+         desc => "The help domain in which help_id is registered" },
+       { name => 'help_id', type => 'string',
+         desc => "The help page's ID" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPlugInManager *manager = gimp->plug_in_manager;
+
+  if (! help_domain && manager->current_plug_in)
+    help_domain = (gchar *)
+      gimp_plug_in_manager_get_help_domain (manager,
+                                            manager->current_plug_in->file,
+                                            NULL);
+
+  gimp_help (gimp, progress, help_domain, help_id);
+}
+CODE
+    );
+}
+
+
+@headers = qw("core/gimp.h"
+              "plug-in/gimpplugin.h"
+              "plug-in/gimppluginmanager.h"
+              "plug-in/gimppluginmanager-help-domain.h");
+
+@procs = qw(help);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Help procedures';
+$doc_title = 'gimphelp';
+$doc_short_desc = 'Loading help pages using gimp_help.';
+$doc_long_desc = 'Loading help pages using gimp_help.';
+
+1;
diff --git a/pdb/groups/image.pdb b/pdb/groups/image.pdb
new file mode 100644
index 0000000..8397f82
--- /dev/null
+++ b/pdb/groups/image.pdb
@@ -0,0 +1,2929 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub image_is_valid {
+    $blurb = 'Returns TRUE if the image is valid.';
+
+    $help = <<'HELP';
+This procedure checks if the given image ID is valid and refers to an
+existing image.
+HELP
+
+    &neo_pdb_misc('2007', '2.4');
+
+    @inargs = (
+        { name => 'image', type => 'image', no_validate => 1,
+          desc => 'The image to check' }
+    );
+
+    @outargs = (
+        { name => 'valid', type => 'boolean',
+          desc => 'Whether the image ID is valid' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  valid = GIMP_IS_IMAGE (image);
+}
+CODE
+    );
+}
+
+sub image_list {
+    $blurb = 'Returns the list of images currently open.';
+
+    $help = <<'HELP';
+This procedure returns the list of images currently open in GIMP.
+HELP
+
+    &std_pdb_misc;
+
+    @outargs = (
+        { name => 'image_ids', type => 'int32array',
+          desc => 'The list of images currently open. The returned value must be freed with g_free()',
+          array => { name => 'num_images',
+                     desc => 'The number of images currently open' } }
+    );
+
+    %invoke = (
+        code => <<CODE
+{
+  GList *list = gimp_get_image_iter (gimp);
+
+  num_images = g_list_length (list);
+
+  if (num_images)
+    {
+      gint i;
+
+      image_ids = g_new (gint32, num_images);
+
+      for (i = 0; i < num_images; i++, list = g_list_next (list))
+        image_ids[i] = gimp_image_get_ID (GIMP_IMAGE (list->data));
+    }
+}
+CODE
+    );
+}
+
+sub image_new {
+    $blurb = 'Creates a new image with the specified width, height, and type.';
+
+    $help = <<'HELP';
+Creates a new image, undisplayed, with the specified extents and
+type. A layer should be created and added before this image is
+displayed, or subsequent calls to gimp_display_new() with this image
+as an argument will fail. Layers can be created using the
+gimp_layer_new() commands. They can be added to an image using the
+gimp_image_insert_layer() command.
+
+
+If your image's type if INDEXED, a colormap must also be added with
+gimp_image_set_colormap(). An indexed image without a colormap will
+output unexpected colors.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'width', type => '1 <= int32 <= GIMP_MAX_IMAGE_SIZE',
+          desc => 'The width of the image' },
+        { name => 'height', type => '1 <= int32 <= GIMP_MAX_IMAGE_SIZE',
+          desc => 'The height of the image' },
+        { name => 'type', type => 'enum GimpImageBaseType',
+          desc => 'The type of image' }
+    );
+
+    @outargs = (
+        { name => 'image', type => 'image',
+          desc => 'The ID of the newly created image' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  image = gimp_create_image (gimp, width, height, type,
+                             GIMP_PRECISION_U8_GAMMA, FALSE);
+
+  if (! image)
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_new_with_precision {
+    $blurb = 'Creates a new image with the specified width, height, type and precision.';
+
+    $help = <<'HELP';
+Creates a new image, undisplayed with the specified extents, type
+and precision. Indexed images can only be created at GIMP_PRECISION_U8_GAMMA
+precision. See gimp_image_new() for further details.
+HELP
+
+    &mitch_pdb_misc('2012', '2.10');
+
+    @inargs = (
+        { name => 'width', type => '1 <= int32 <= GIMP_MAX_IMAGE_SIZE',
+          desc => 'The width of the image' },
+        { name => 'height', type => '1 <= int32 <= GIMP_MAX_IMAGE_SIZE',
+          desc => 'The height of the image' },
+        { name => 'type', type => 'enum GimpImageBaseType',
+          desc => 'The type of image' },
+        { name => 'precision', type => 'enum GimpPrecision',
+          desc => 'The precision' }
+    );
+
+    @outargs = (
+        { name => 'image', type => 'image',
+          desc => 'The ID of the newly created image' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp->plug_in_manager->current_plug_in)
+    gimp_plug_in_enable_precision (gimp->plug_in_manager->current_plug_in);
+
+  if (type != GIMP_INDEXED || precision == GIMP_PRECISION_U8_GAMMA)
+    {
+      image = gimp_create_image (gimp, width, height, type,
+                                 precision, FALSE);
+      if (! image)
+        success = FALSE;
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_duplicate {
+    $blurb = 'Duplicate the specified image';
+
+    $help = <<'HELP';
+This procedure duplicates the specified image, copying all layers, channels,
+and image information.
+HELP
+
+    &std_pdb_misc;
+    $date = '1997';
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+    );
+
+    @outargs = (
+        { name => 'new_image', type => 'image',
+          desc => 'The new, duplicated image' }
+    );
+
+    %invoke = (
+        headers => [ qw("core/gimpimage-duplicate.h") ],
+        code => <<'CODE'
+{
+  new_image = gimp_image_duplicate (image);
+
+  if (! new_image)
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_delete {
+    $blurb = 'Delete the specified image.';
+
+    $help = <<'HELP';
+If there are no displays associated with this image it will be deleted.
+This means that you can not delete an image through the PDB that was
+created by the user. If the associated display was however created
+through the PDB and you know the display ID, you may delete the display.
+Removal of the last associated display will then delete the image.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_image_get_display_count (image) == 0)
+    g_object_unref (image);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_free_shadow {
+    &std_pdb_deprecated ('gimp-drawable-free-shadow');
+
+    @inargs = (
+        { name => 'image', type => 'image', dead => 1,
+          desc => 'The image' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+}
+CODE
+    );
+}
+
+sub image_get_layers {
+    $blurb = 'Returns the list of layers contained in the specified image.';
+
+    $help = <<HELP;
+This procedure returns the list of layers contained in the specified image.
+The order of layers is from topmost to bottommost.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+    );
+
+    @outargs = (
+        { name => 'layer_ids', type => 'int32array',
+          desc => 'The list of layers contained in the image. The returned value must be freed with 
g_free()',
+          array => { name => 'num_layers',
+                     desc => 'The number of layers contained in the image' } }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GList *list = gimp_image_get_layer_iter (image);
+
+  num_layers = g_list_length (list);
+
+  if (num_layers)
+    {
+      gint i;
+
+      layer_ids = g_new (gint32, num_layers);
+
+      for (i = 0; i < num_layers; i++, list = g_list_next (list))
+        layer_ids[i] = gimp_item_get_ID (GIMP_ITEM (list->data));
+    }
+}
+CODE
+    );
+}
+
+sub image_get_channels {
+    $blurb = 'Returns the list of channels contained in the specified image.';
+
+    $help = <<HELP;
+This procedure returns the list of channels contained in the specified image.
+This does not include the selection mask, or layer masks. The order is from
+topmost to bottommost. Note that "channels" are custom channels and do not
+include the image's color components.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+    );
+
+    @outargs = (
+        { name => 'channel_ids', type => 'int32array',
+          desc => 'The list of channels contained in the image. The returned value must be freed with 
g_free()',
+          array => { name => 'num_channels',
+                     desc => 'The number of channels contained in the image' } }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GList *list = gimp_image_get_channel_iter (image);
+
+  num_channels = g_list_length (list);
+
+  if (num_channels)
+    {
+      gint i;
+
+      channel_ids = g_new (gint32, num_channels);
+
+      for (i = 0; i < num_channels; i++, list = g_list_next (list))
+        channel_ids[i] = gimp_item_get_ID (GIMP_ITEM (list->data));
+    }
+}
+CODE
+    );
+}
+
+sub image_get_vectors {
+    $blurb = 'Returns the list of vectors contained in the specified image.';
+
+    $help = <<HELP;
+This procedure returns the list of vectors contained in the specified image.
+HELP
+
+    &simon_pdb_misc('2005', '2.4');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+    );
+
+    @outargs = (
+        { name => 'vector_ids', type => 'int32array',
+          desc => 'The list of vectors contained in the image. The returned value must be freed with 
g_free()',
+          array => { name => 'num_vectors',
+                     desc => 'The number of vectors contained in the image' } }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GList *list = gimp_image_get_vectors_iter (image);
+
+  num_vectors = g_list_length (list);
+
+  if (num_vectors)
+    {
+      gint i;
+
+      vector_ids = g_new (gint32, num_vectors);
+
+      for (i = 0; i < num_vectors; i++, list = g_list_next (list))
+        vector_ids[i] = gimp_item_get_ID (GIMP_ITEM (list->data));
+    }
+}
+CODE
+    );
+}
+
+sub image_unset_active_channel {
+    $blurb = 'Unsets the active channel in the specified image.';
+
+    $help = <<'HELP';
+If an active channel exists, it is unset. There then exists no active
+channel, and if desired, one can be set through a call to 'Set Active
+Channel'. No error is returned in the case of no existing active
+channel.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  gimp_image_unset_active_channel (image);
+}
+CODE
+    );
+}
+
+sub image_pick_color {
+    $blurb = <<'BLURB';
+Determine the color at the given drawable coordinates
+BLURB
+
+    $help = <<'HELP';
+This tool determines the color at the specified coordinates. The
+returned color is an RGB triplet even for grayscale and indexed
+drawables. If the coordinates lie outside of the extents of the
+specified drawable, then an error is returned. If the drawable has an
+alpha channel, the algorithm examines the alpha value of the drawable
+at the coordinates. If the alpha value is completely transparent (0),
+then an error is returned. If the sample_merged parameter is TRUE,
+the data of the composite image will be used instead of that for the
+specified drawable. This is equivalent to sampling for colors after
+merging all visible layers. In the case of a merged sampling, the
+supplied drawable is ignored.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'drawable', type => 'drawable', none_ok => 1,
+          desc => 'The drawable to pick from' },
+        { name => 'x', type => 'float',
+          desc => 'x coordinate of upper-left corner of rectangle' },
+        { name => 'y', type => 'float',
+          desc => 'y coordinate of upper-left corner of rectangle' },
+        { name => 'sample_merged', type => 'boolean',
+          desc => 'Use the composite image, not the drawable' },
+        { name => 'sample_average', type => 'boolean',
+          desc => 'Average the color of all the pixels in a specified
+                   radius' },
+        { name => 'average_radius', type => '0 < float', no_validate => 1,
+          desc => 'The radius of pixels to average' }
+    );
+
+    @outargs = (
+        { name => 'color', type => 'color', has_alpha => 1, void_ret => 1,
+          desc => 'The return color' }
+    );
+
+    %invoke = (
+        headers => [ qw("core/gimpimage-pick-color.h" "core/gimppickable.h") ],
+        code => <<'CODE'
+{
+  if (!sample_merged)
+    if (!drawable || (gimp_item_get_image (GIMP_ITEM (drawable)) != image))
+      success = FALSE;
+
+  if (success && sample_average)
+    {
+      if (average_radius <= 0.0)
+        success = FALSE;
+    }
+
+  if (success)
+    {
+      if (sample_merged)
+        gimp_pickable_flush (GIMP_PICKABLE (image));
+      else
+        gimp_pickable_flush (GIMP_PICKABLE (drawable));
+
+      success = gimp_image_pick_color (image,
+                                       drawable,
+                                       (gint) x, (gint) y,
+                                       sample_merged,
+                                       sample_average,
+                                       average_radius,
+                                       NULL,
+                                       NULL,
+                                       &color);
+    }
+}
+CODE
+    );
+}
+
+sub image_pick_correlate_layer {
+    $blurb = 'Find the layer visible at the specified coordinates.';
+
+    $help = <<'HELP';
+This procedure finds the layer which is visible at the specified
+coordinates.  Layers which do not qualify are those whose extents do
+not pass within the specified coordinates, or which are transparent at
+the specified coordinates.  This procedure will return -1 if no layer
+is found.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'x', type => 'int32',
+          desc => 'The x coordinate for the pick' },
+        { name => 'y', type => 'int32',
+          desc => 'The y coordinate for the pick' }
+    );
+
+    @outargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The layer found at the specified coordinates' }
+    );
+
+    %invoke = (
+        headers => [ qw("core/gimpimage-pick-item.h") ],
+        code => <<'CODE'
+{
+  layer = gimp_image_pick_layer (image, x, y);
+}
+CODE
+    );
+}
+
+sub image_get_item_position {
+    $blurb = 'Returns the position of the item in its level of its item tree.';
+
+    $help = <<'HELP';
+This procedure determines the position of the specified item in its
+level in its item tree in the image. If the item doesn't exist in the
+image, or the item is not part of an item tree, an error is returned.
+HELP
+
+    &mitch_pdb_misc('2010', '2.8');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'item', type => 'item',
+          desc => 'The item' }
+    );
+
+    @outargs = (
+        { name => 'position', type => 'int32',
+          desc => "The position of the item in its level in the item tree" }
+    );
+
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_item_is_in_tree (item, image, 0, error))
+    position = gimp_item_get_index (item);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_raise_item {
+    $blurb = "Raise the specified item in its level in its item tree";
+
+    $help = <<'HELP';
+This procedure raises the specified item one step in the item tree.
+The procecure call will fail if there is no item above it.
+HELP
+
+    &mitch_pdb_misc('2010', '2.8');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'item', type => 'item',
+          desc => 'The item to raise' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_item_is_in_tree (item, image, 0, error))
+    success = gimp_image_raise_item (image, item, error);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_lower_item {
+    $blurb = "Lower the specified item in its level in its item tree";
+
+    $help = <<'HELP';
+This procedure lowers the specified item one step in the item tree.
+The procecure call will fail if there is no item below it.
+HELP
+
+    &mitch_pdb_misc('2010', '2.8');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'item', type => 'item',
+          desc => 'The item to lower' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_item_is_in_tree (item, image, 0, error))
+    success = gimp_image_lower_item (image, item, error);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_raise_item_to_top {
+    $blurb = <<'BLURB';
+Raise the specified item to the top of its level in its item tree
+BLURB
+
+    $help = <<'HELP';
+This procedure raises the specified item to top of its level in the
+item tree. It will not move the item if there is no item above it.
+HELP
+
+    &mitch_pdb_misc('2010', '2.8');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'item', type => 'item',
+          desc => 'The item to raise to top' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_item_is_in_tree (item, image, 0, error))
+    success = gimp_image_raise_item_to_top (image, item);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_lower_item_to_bottom {
+    $blurb = <<'BLURB';
+Lower the specified item to the bottom of its level in its item tree
+BLURB
+
+    $help = <<'HELP';
+This procedure lowers the specified item to bottom of its level in the
+item tree. It will not move the layer if there is no layer below it.
+HELP
+
+    &mitch_pdb_misc('2010', '2.8');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'item', type => 'item',
+          desc => 'The item to lower to bottom' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_item_is_in_tree (item, image, 0, error))
+    success = gimp_image_lower_item_to_bottom (image, item);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_reorder_item {
+    $blurb = "Reorder the specified item within its item tree";
+
+    $help = <<'HELP';
+This procedure reorders the specified item within its item tree.
+HELP
+
+    &mitch_pdb_misc('2010', '2.8');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'item', type => 'item',
+          desc => 'The item to reorder' },
+        { name => 'parent', type => 'item', none_ok => 1,
+          desc => 'The new parent item' },
+        { name => 'position', type => 'int32',
+          desc => 'The new position of the item' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_item_is_in_tree (item, image, 0, error) &&
+      (parent == NULL ||
+       (gimp_pdb_item_is_in_same_tree (item, parent, image, error) &&
+        gimp_pdb_item_is_group (parent, error) &&
+        gimp_pdb_item_is_not_ancestor (item, parent, error))))
+    {
+      success = gimp_image_reorder_item (image, item, parent, position,
+                                         TRUE, NULL);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_merge_visible_layers {
+    $blurb = 'Merge the visible image layers into one.';
+
+    $help = <<'HELP';
+This procedure combines the visible layers into a single layer using
+the specified merge type. A merge type of EXPAND_AS_NECESSARY expands
+the final layer to encompass the areas of the visible layers. A merge
+type of CLIP_TO_IMAGE clips the final layer to the extents of the
+image. A merge type of CLIP_TO_BOTTOM_LAYER clips the final layer to
+the size of the bottommost layer.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'merge_type',
+          type => 'enum GimpMergeType (no GIMP_FLATTEN_IMAGE)',
+          desc => 'The type of merge' }
+    );
+
+    @outargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The resulting layer' }
+    );
+
+    %invoke = (
+        headers => [ qw("core/gimpimage-merge.h") ],
+        code => <<'CODE'
+{
+  layer = gimp_image_merge_visible_layers (image, context, merge_type,
+                                           FALSE, FALSE);
+
+  if (! layer)
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_merge_down {
+    $blurb = 'Merge the layer passed and the first visible layer below.';
+
+    $help = <<'HELP';
+This procedure combines the passed layer and the first visible layer
+below it using the specified merge type. A merge type of
+EXPAND_AS_NECESSARY expands the final layer to encompass the areas of
+the visible layers. A merge type of CLIP_TO_IMAGE clips the final
+layer to the extents of the image. A merge type of
+CLIP_TO_BOTTOM_LAYER clips the final layer to the size of the
+bottommost layer.
+HELP
+
+    &larry_pdb_misc('1998');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'merge_layer', type => 'layer',
+          desc => 'The layer to merge down from' },
+        { name => 'merge_type',
+          type => 'enum GimpMergeType (no GIMP_FLATTEN_IMAGE)',
+          desc => 'The type of merge' }
+    );
+
+    @outargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The resulting layer' }
+    );
+
+    %invoke = (
+        headers => [ qw("core/gimpimage-merge.h") ],
+        code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (merge_layer), image, 0, error))
+    {
+      layer = gimp_image_merge_down (image, merge_layer, context, merge_type,
+                                     error);
+
+      if (! layer)
+        success = FALSE;
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_flatten {
+    $blurb = <<'BLURB';
+Flatten all visible layers into a single layer. Discard all invisible layers.
+BLURB
+
+    $help = <<'HELP';
+This procedure combines the visible layers in a manner analogous to merging
+with the CLIP_TO_IMAGE merge type. Non-visible layers are discarded, and the
+resulting image is stripped of its alpha channel.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+   );
+
+    @outargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The resulting layer' }
+    );
+
+    %invoke = (
+        headers => [ qw("core/gimpimage-merge.h") ],
+        code => <<'CODE'
+{
+  layer = gimp_image_flatten (image, context, error);
+
+  if (! layer)
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_add_layer {
+    &std_pdb_deprecated ('gimp-image-insert-layer');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'layer', type => 'layer',
+          desc => 'The layer' },
+        { name => 'position', type => 'int32',
+          desc => 'The layer position' }
+    );
+
+    $invoke{code} = <<'CODE';
+{
+  if (gimp_pdb_item_is_floating (GIMP_ITEM (layer), image, error) &&
+      gimp_pdb_image_is_base_type (image,
+                                   gimp_drawable_get_base_type (GIMP_DRAWABLE (layer)),
+                                   error))
+    {
+      success = gimp_image_add_layer (image, layer,
+                                      NULL, MAX (position, -1), TRUE);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+}
+
+sub image_insert_layer {
+    $blurb = 'Add the specified layer to the image.';
+
+    $help = <<'HELP';
+This procedure adds the specified layer to the image at the given
+position. If the specified parent is a valid layer group (See
+gimp_item_is_group() and gimp_layer_group_new()) then the layer is
+added inside the group. If the parent is 0, the layer is added inside
+the main stack, outside of any group. The position argument specifies
+the location of the layer inside the stack (or the group, if a valid
+parent was supplied), starting from the top (0) and increasing.  If
+the position is specified as -1 and the parent is specified as 0, then
+the layer is inserted above the active layer, or inside the group if
+the active layer is a layer group. The layer type must be compatible
+with the image base type.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'layer', type => 'layer',
+          desc => 'The layer' },
+        { name => 'parent', type => 'layer', none_ok => 1,
+          desc => 'The parent layer' },
+        { name => 'position', type => 'int32',
+          desc => 'The layer position' }
+    );
+
+    $invoke{code} = <<'CODE';
+{
+  if (gimp_pdb_item_is_floating (GIMP_ITEM (layer), image, error) &&
+      gimp_pdb_image_is_base_type (image,
+                                   gimp_drawable_get_base_type (GIMP_DRAWABLE (layer)),
+                                   error) &&
+      (parent == NULL ||
+       (gimp_pdb_item_is_in_tree (GIMP_ITEM (parent), image, 0, error) &&
+        gimp_pdb_item_is_group (GIMP_ITEM (parent), error))))
+    {
+      if (position == -1 && parent == NULL)
+        parent = GIMP_IMAGE_ACTIVE_PARENT;
+
+      success = gimp_image_add_layer (image, layer,
+                                      parent, MAX (position, -1), TRUE);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+}
+
+sub image_remove_layer {
+    $blurb = 'Remove the specified layer from the image.';
+
+    $help = <<'HELP';
+This procedure removes the specified layer from the image. If the
+layer doesn't exist, an error is returned. If there are no layers left
+in the image, this call will fail. If this layer is the last layer
+remaining, the image will become empty and have no active layer.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'layer', type => 'layer',
+          desc => 'The layer' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (layer), image, 0, error))
+    gimp_image_remove_layer (image, layer, TRUE, NULL);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_add_layer_mask {
+    &std_pdb_deprecated ('gimp-layer-add-mask');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'layer', type => 'layer',
+          desc => 'The layer to receive the mask' },
+        { name => 'mask', type => 'layer_mask',
+          desc => 'The mask to add to the layer' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_item_is_floating (GIMP_ITEM (mask), image, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (layer), error))
+    success = (gimp_layer_add_mask (layer, mask, TRUE, error) == mask);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_remove_layer_mask {
+    &std_pdb_deprecated ('gimp-layer-remove-mask');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'layer', type => 'layer',
+          desc => 'The layer from which to remove mask' },
+        { name => 'mode', type => 'enum GimpMaskApplyMode',
+          desc => 'Removal mode' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpPDBItemModify modify = 0;
+
+  if (mode == GIMP_MASK_APPLY)
+    modify |= GIMP_PDB_ITEM_CONTENT;
+
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (layer), image, modify, error) &&
+      gimp_layer_get_mask (layer))
+    gimp_layer_apply_mask (layer, mode, TRUE);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_add_channel {
+    &std_pdb_deprecated ('gimp-image-insert-channel');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'channel', type => 'channel',
+          desc => 'The channel' },
+        { name => 'position', type => 'int32',
+          desc => 'The channel position' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_item_is_floating (GIMP_ITEM (channel), image, error))
+    {
+      success = gimp_image_add_channel (image, channel,
+                                        NULL, MAX (position, -1), TRUE);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub image_insert_channel {
+    $blurb = 'Add the specified channel to the image.';
+
+    $help = <<'HELP';
+This procedure adds the specified channel to the image at the given
+position. Since channel groups are not currently supported, the parent
+argument must always be 0. The position argument specifies the
+location of the channel inside the stack, starting from the top (0) and
+increasing. If the position is specified as -1, then the channel is
+inserted above the active channel.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'channel', type => 'channel',
+          desc => 'The channel' },
+        { name => 'parent', type => 'channel', none_ok => 1,
+          desc => 'The parent channel' },
+        { name => 'position', type => 'int32',
+          desc => 'The channel position' }
+    );
+
+    $invoke{code} = <<'CODE';
+{
+  if (gimp_pdb_item_is_floating (GIMP_ITEM (channel), image, error) &&
+      (parent == NULL ||
+       (gimp_pdb_item_is_in_tree (GIMP_ITEM (parent), image, 0, error) &&
+        gimp_pdb_item_is_group (GIMP_ITEM (parent), error))))
+    {
+      if (position == -1 && parent == NULL)
+        parent = GIMP_IMAGE_ACTIVE_PARENT;
+
+      success = gimp_image_add_channel (image, channel,
+                                        parent, MAX (position, -1), TRUE);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+}
+
+sub image_remove_channel {
+    $blurb = 'Remove the specified channel from the image.';
+
+    $help = <<'HELP';
+This procedure removes the specified channel from the image. If the channel
+doesn't exist, an error is returned.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'channel', type => 'channel',
+          desc => 'The channel' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (channel), image, 0, error))
+    gimp_image_remove_channel (image, channel, TRUE, NULL);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_add_vectors {
+    &std_pdb_deprecated ('gimp-image-insert-vectors');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'vectors', type => 'vectors',
+          desc => 'The vectors object' },
+        { name => 'position', type => 'int32',
+          desc => 'The vectors objects position' }
+    );
+
+    $invoke{code} = <<'CODE';
+{
+  if (gimp_pdb_item_is_floating (GIMP_ITEM (vectors), image, error))
+    {
+      success = gimp_image_add_vectors (image, vectors,
+                                        NULL, MAX (position, -1), TRUE);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+}
+
+sub image_insert_vectors {
+    $blurb = 'Add the specified vectors to the image.';
+
+    $help = <<'HELP';
+This procedure adds the specified vectors to the image at the given
+position. Since vectors groups are not currently supported, the parent
+argument must always be 0. The position argument specifies the
+location of the vectors inside the stack, starting from the top (0) and
+increasing. If the position is specified as -1, then the vectors is
+inserted above the active vectors.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'vectors', type => 'vectors',
+          desc => 'The vectors' },
+        { name => 'parent', type => 'vectors', none_ok => 1,
+          desc => 'The parent vectors' },
+        { name => 'position', type => 'int32',
+          desc => 'The vectors position' }
+    );
+
+    $invoke{code} = <<'CODE';
+{
+  if (gimp_pdb_item_is_floating (GIMP_ITEM (vectors), image, error) &&
+      (parent == NULL ||
+       (gimp_pdb_item_is_in_tree (GIMP_ITEM (parent), image, 0, error) &&
+        gimp_pdb_item_is_group (GIMP_ITEM (parent), error))))
+    {
+      if (position == -1 && parent == NULL)
+        parent = GIMP_IMAGE_ACTIVE_PARENT;
+
+      success = gimp_image_add_vectors (image, vectors,
+                                        parent, MAX (position, -1), TRUE);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+}
+
+sub image_remove_vectors {
+    $blurb = 'Remove the specified path from the image.';
+
+    $help = <<'HELP';
+This procedure removes the specified path from the image. If the path
+doesn't exist, an error is returned.
+HELP
+
+    &simon_pdb_misc('2005', '2.4');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'vectors', type => 'vectors',
+          desc => 'The vectors object' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (vectors), image, 0, error))
+    gimp_image_remove_vectors (image, vectors, TRUE, NULL);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_get_active_drawable {
+    $blurb = "Get the image's active drawable";
+
+    $help = <<'HELP';
+This procedure returns the ID of the image's active drawable. This can be
+either a layer, a channel, or a layer mask. The active drawable is specified by
+the active image channel. If that is -1, then by the active image layer. If the
+active image layer has a layer mask and the layer mask is in edit mode, then
+the layer mask is the active drawable.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+    );
+
+    @outargs = (
+        { name => 'drawable', type => 'drawable',
+          desc => 'The active drawable' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  drawable = gimp_image_get_active_drawable (image);
+}
+CODE
+    );
+}
+
+sub image_base_type {
+    $blurb = 'Get the base type of the image.';
+
+    $help = <<'HELP';
+This procedure returns the image's base type. Layers in the image must be of
+this subtype, but can have an optional alpha channel.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+    );
+
+    @outargs = (
+        { name => 'base_type', type => 'enum GimpImageBaseType',
+          desc => "The image's base type" }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  base_type = gimp_image_get_base_type (image);
+}
+CODE
+    );
+}
+
+sub image_get_precision {
+    $blurb = 'Get the precision of the image.';
+
+    $help = <<'HELP';
+This procedure returns the image's precision.
+HELP
+
+    &mitch_pdb_misc('2012', '2.10');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+    );
+
+    @outargs = (
+        { name => 'precision', type => 'enum GimpPrecision',
+          desc => "The image's precision" }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp->plug_in_manager->current_plug_in)
+    gimp_plug_in_enable_precision (gimp->plug_in_manager->current_plug_in);
+
+  precision = gimp_image_get_precision (image);
+}
+CODE
+    );
+}
+
+sub image_get_default_new_layer_mode {
+    $blurb = 'Get the default mode for newly created layers of this image.';
+    $help  = 'Returns the default mode for newly created layers of this image.';
+
+    &mitch_pdb_misc('2017', '2.10');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+    );
+
+    @outargs = (
+       { name => 'mode', type => 'enum GimpLayerMode',
+          default => 'GIMP_LAYER_MODE_NORMAL',
+         desc => 'The layer mode' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  mode = gimp_image_get_default_new_layer_mode (image);
+}
+CODE
+    );
+}
+
+sub image_get_colormap {
+    $blurb = "Returns the image's colormap";
+
+    $help = <<'HELP';
+This procedure returns an actual pointer to the image's colormap, as well as
+the number of bytes contained in the colormap. The actual number of colors in
+the transmitted colormap will be 'num-bytes' / 3. If the image is not in
+Indexed color mode, no colormap is returned.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+    );
+
+    @outargs = (
+        { name => 'colormap', type => 'int8array', wrap => 1,
+          desc => "The image's colormap. The returned value must be freed with g_free()",
+          array => { name => 'num_bytes',
+                     desc => 'Number of bytes in the colormap array' } }
+    );
+
+    %invoke = (
+        headers => [ qw("core/gimpimage-colormap.h") ],
+        code => <<'CODE'
+{
+  num_bytes = 3 * gimp_image_get_colormap_size (image);
+  colormap = g_memdup (gimp_image_get_colormap (image), num_bytes);
+}
+CODE
+    );
+}
+
+sub image_set_colormap {
+    $blurb = "Sets the entries in the image's colormap.";
+
+    $help = <<'HELP';
+This procedure sets the entries in the specified image's colormap. The number
+of entries is specified by the 'num-bytes' parameter and corresponds to the
+number of INT8 triples that must be contained in the 'colormap' array. The
+actual number of colors in the transmitted colormap is 'num-bytes' / 3.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'colormap', type => 'int8array', wrap => 1,
+          desc => 'The new colormap values',
+          array => { name => 'num_bytes', type => '0 <= int32 <= 768',
+                     desc => 'Number of bytes in the colormap array' } }
+    );
+
+    %invoke = (
+        headers => [ qw("core/gimpimage-colormap.h") ],
+        code => <<'CODE'
+{
+  gimp_image_set_colormap (image, colormap, num_bytes / 3, TRUE);
+}
+CODE
+    );
+}
+
+sub image_get_metadata {
+    $blurb = "Returns the image's metadata.";
+    $help  = 'Returns exif/iptc/xmp metadata from the image.';
+
+    &std_pdb_misc('2013', '2.10');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+    );
+
+    @outargs = (
+       { name => 'metadata_string', type => 'string', wrap => 1,
+         desc => 'The exif/ptc/xmp metadata as a string'}
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpMetadata *metadata = gimp_image_get_metadata (image);
+
+  if (metadata)
+    metadata_string = gimp_metadata_serialize (metadata);
+}
+CODE
+    );
+}
+
+sub image_set_metadata {
+    $blurb = "Set the image's metadata.";
+    $help  = 'Sets exif/iptc/xmp metadata on the image.';
+
+    &std_pdb_misc('2013', '2.10');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+       { name => 'metadata_string', type => 'string', wrap => 1,
+         desc => 'The exif/ptc/xmp metadata as a string' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpMetadata *metadata = gimp_metadata_deserialize (metadata_string);
+
+  gimp_image_set_metadata (image, metadata, TRUE);
+
+  if (metadata)
+    g_object_unref (metadata);
+}
+CODE
+    );
+}
+
+sub image_clean_all {
+    $blurb = 'Set the image dirty count to 0.';
+
+    $help = <<'HELP';
+This procedure sets the specified image's dirty count to 0, allowing
+operations to occur without having a 'dirtied' image. This is
+especially useful for creating and loading images which should not
+initially be considered dirty, even though layers must be created,
+filled, and installed in the image. Note that save plug-ins must NOT
+call this function themselves after saving the image.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  gimp_image_clean_all (image);
+}
+CODE
+    );
+}
+
+sub image_is_dirty {
+    $blurb = 'Checks if the image has unsaved changes.';
+
+    $help = <<'HELP';
+This procedure checks the specified image's dirty count to see if it
+needs to be saved. Note that saving the image does not automatically
+set the dirty count to 0, you need to call gimp_image_clean_all() after
+calling a save procedure to make the image clean.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+    );
+
+    @outargs = (
+        { name => 'dirty', type => 'boolean',
+          desc => 'TRUE if the image has unsaved changes.' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  dirty = gimp_image_is_dirty (image);
+}
+CODE
+    );
+}
+
+sub image_get_floating_sel {
+    $blurb = 'Return the floating selection of the image.';
+
+    $help = <<'HELP';
+This procedure returns the image's floating selection, if it exists.
+If it doesn't exist, -1 is returned as the layer ID.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+    );
+
+    @outargs = (
+        { name => 'floating_sel', type => 'layer',
+          desc => "The image's floating selection" }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  floating_sel = gimp_image_get_floating_selection (image);
+}
+CODE
+    );
+}
+
+sub image_floating_sel_attached_to {
+    $blurb = 'Return the drawable the floating selection is attached to.';
+
+    $help = <<'HELP';
+This procedure returns the drawable the image's floating selection is attached
+to, if it exists. If it doesn't exist, -1 is returned as the drawable ID.
+HELP
+
+    &wolfgang_pdb_misc('1998');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+    );
+
+    @outargs = (
+        { name => 'drawable', type => 'drawable',
+          desc => 'The drawable the floating selection is attached to' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpLayer *floating_sel = gimp_image_get_floating_selection (image);
+
+  if (floating_sel)
+    drawable = gimp_layer_get_floating_sel_drawable (floating_sel);
+  else
+    drawable = NULL;
+}
+CODE
+    );
+}
+
+sub image_width {
+    $blurb = 'Return the width of the image';
+
+    $help = <<'HELP';
+This procedure returns the image's width. This value is independent of any of
+the layers in this image. This is the "canvas" width.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+    );
+
+    @outargs = (
+        { name => 'width', type => 'int32',
+          desc => "The image's width" }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  width = gimp_image_get_width (image);
+}
+CODE
+    );
+}
+
+sub image_height {
+    $blurb = 'Return the height of the image';
+
+    $help = <<'HELP';
+This procedure returns the image's height. This value is independent of any of
+the layers in this image. This is the "canvas" height.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+    );
+
+    @outargs = (
+        { name => 'height', type => 'int32',
+          desc => "The image's height" }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  height = gimp_image_get_height (image);
+}
+CODE
+    );
+}
+
+sub image_get_active_layer {
+    $blurb = "Returns the specified image's active layer.";
+
+    $help = <<'HELP';
+If there is an active layer, its ID will be returned, otherwise, -1.  If a
+channel is currently active, then no layer will be. If a layer mask is active,
+then this will return the associated layer.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+    );
+
+    @outargs = (
+        { name => 'active_layer', type => 'layer',
+          desc => 'The active layer' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  active_layer = gimp_image_get_active_layer (image);
+}
+CODE
+    );
+}
+
+sub image_set_active_layer {
+    $blurb = "Sets the specified image's active layer.";
+
+    $help = <<'HELP';
+If the layer exists, it is set as the active layer in the image. Any
+previous active layer or channel is set to inactive. An exception is a
+previously existing floating selection, in which case this procedure
+will return an execution error.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'active_layer', type => 'layer',
+          desc => 'The new image active layer' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_image_set_active_layer (image, active_layer) != active_layer)
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_get_active_channel {
+    $blurb = "Returns the specified image's active channel.";
+
+    $help = <<'HELP';
+If there is an active channel, this will return the channel ID, otherwise, -1.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+    );
+
+    @outargs = (
+        { name => 'active_channel', type => 'channel',
+          desc => 'The active channel' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  active_channel = gimp_image_get_active_channel (image);
+}
+CODE
+    );
+}
+
+sub image_set_active_channel {
+    $blurb = "Sets the specified image's active channel.";
+
+    $help = <<'HELP';
+If the channel exists, it is set as the active channel in the
+image. Any previous active channel or layer is set to inactive. An
+exception is a previously existing floating selection, in which case
+this procedure will return an execution error.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'active_channel', type => 'channel',
+          desc => 'The new image active channel' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_image_set_active_channel (image, active_channel) != active_channel)
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_get_active_vectors {
+    $blurb = "Returns the specified image's active vectors.";
+
+    $help = <<'HELP';
+If there is an active path, its ID will be returned, otherwise, -1.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+    );
+
+    @outargs = (
+        { name => 'active_vectors', type => 'vectors',
+          desc => 'The active vectors' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  active_vectors = gimp_image_get_active_vectors (image);
+}
+CODE
+    );
+}
+
+sub image_set_active_vectors {
+    $blurb = "Sets the specified image's active vectors.";
+
+    $help = <<'HELP';
+If the path exists, it is set as the active path in the image.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'active_vectors', type => 'vectors',
+          desc => 'The new image active vectors' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_image_set_active_vectors (image, active_vectors) != active_vectors)
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_get_selection {
+    $blurb = "Returns the specified image's selection.";
+
+    $help = <<'HELP';
+This will always return a valid ID for a selection -- which is represented as a
+channel internally.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+    );
+
+    @outargs = (
+        { name => 'selection', type => 'selection',
+          desc => 'The selection channel' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  selection = GIMP_SELECTION (gimp_image_get_mask (image));
+
+  if (! selection)
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_get_component_active {
+    $blurb = "Returns if the specified image's image component is active.";
+
+    $help = <<'HELP';
+This procedure returns if the specified image's image component
+(i.e. Red, Green, Blue intensity channels in an RGB image) is active
+or inactive -- whether or not it can be modified. If the specified
+component is not valid for the image type, an error is returned.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'component', type => 'enum GimpChannelType',
+          desc => 'The image component' }
+    );
+
+    @outargs = (
+        { name => 'active', type => 'boolean',
+          desc => 'Component is active' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (component == GIMP_CHANNEL_GRAY)
+    success = gimp_pdb_image_is_base_type (image, GIMP_GRAY, error);
+  else if (component == GIMP_CHANNEL_INDEXED)
+    success = gimp_pdb_image_is_base_type (image, GIMP_INDEXED, error);
+  else
+    success = gimp_pdb_image_is_base_type (image, GIMP_RGB, error);
+
+  if (success)
+    active = gimp_image_get_component_active (image, component);
+}
+CODE
+    );
+}
+
+sub image_set_component_active {
+    $blurb = "Sets if the specified image's image component is active.";
+
+    $help = <<'HELP';
+This procedure sets if the specified image's image component
+(i.e. Red, Green, Blue intensity channels in an RGB image) is active
+or inactive -- whether or not it can be modified. If the specified
+component is not valid for the image type, an error is returned.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'component', type => 'enum GimpChannelType',
+          desc => 'The image component' },
+        { name => 'active', type => 'boolean',
+          desc => 'Component is active' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (component == GIMP_CHANNEL_GRAY)
+    success = gimp_pdb_image_is_base_type (image, GIMP_GRAY, error);
+  else if (component == GIMP_CHANNEL_INDEXED)
+    success = gimp_pdb_image_is_base_type (image, GIMP_INDEXED, error);
+  else
+    success = gimp_pdb_image_is_base_type (image, GIMP_RGB, error);
+
+  if (success)
+    gimp_image_set_component_active (image, component, active);
+}
+CODE
+    );
+}
+
+sub image_get_component_visible {
+    $blurb = "Returns if the specified image's image component is visible.";
+
+    $help = <<'HELP';
+This procedure returns if the specified image's image component
+(i.e. Red, Green, Blue intensity channels in an RGB image) is visible
+or invisible -- whether or not it can be seen. If the specified
+component is not valid for the image type, an error is returned.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'component', type => 'enum GimpChannelType',
+          desc => 'The image component' }
+    );
+
+    @outargs = (
+        { name => 'visible', type => 'boolean',
+          desc => 'Component is visible' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (component == GIMP_CHANNEL_GRAY)
+    success = gimp_pdb_image_is_base_type (image, GIMP_GRAY, error);
+  else if (component == GIMP_CHANNEL_INDEXED)
+    success = gimp_pdb_image_is_base_type (image, GIMP_INDEXED, error);
+  else
+    success = gimp_pdb_image_is_base_type (image, GIMP_RGB, error);
+
+  if (success)
+    visible = gimp_image_get_component_visible (image, component);
+}
+CODE
+    );
+}
+
+sub image_set_component_visible {
+    $blurb = "Sets if the specified image's image component is visible.";
+
+    $help = <<'HELP';
+This procedure sets if the specified image's image component
+(i.e. Red, Green, Blue intensity channels in an RGB image) is visible
+or invisible -- whether or not it can be seen. If the specified
+component is not valid for the image type, an error is returned.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'component', type => 'enum GimpChannelType',
+          desc => 'The image component' },
+        { name => 'visible', type => 'boolean',
+          desc => 'Component is visible' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (component == GIMP_CHANNEL_GRAY)
+    success = gimp_pdb_image_is_base_type (image, GIMP_GRAY, error);
+  else if (component == GIMP_CHANNEL_INDEXED)
+    success = gimp_pdb_image_is_base_type (image, GIMP_INDEXED, error);
+  else
+    success = gimp_pdb_image_is_base_type (image, GIMP_RGB, error);
+
+  if (success)
+    gimp_image_set_component_visible (image, component, visible);
+}
+CODE
+    );
+}
+
+sub image_get_filename {
+    $blurb = "Returns the specified image's filename.";
+
+    $help = <<'HELP';
+This procedure returns the specified image's filename in the
+filesystem encoding. The image has a filename only if it was loaded or
+imported from a file or has since been saved or exported. Otherwise,
+this function returns %NULL. See also gimp_image_get_uri().
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+    );
+
+    @outargs = (
+        { name => 'filename', type => 'string',
+          desc => 'The filename. The returned value must be freed with g_free()' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GFile *file = gimp_image_get_any_file (image);
+  if (file)
+    filename = g_file_get_path (file);
+}
+CODE
+    );
+}
+
+sub image_set_filename {
+    $blurb = "Sets the specified image's filename.";
+
+    $help = <<'HELP';
+This procedure sets the specified image's filename. The filename
+should be in the filesystem encoding.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'filename', type => 'string',
+          desc => 'The new image filename', allow_non_utf8 => 1 }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  /*  verify that the filename can be converted to UTF-8 and back  */
+  gchar *utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, error);
+
+  if (utf8)
+    {
+      gchar *tmp = g_filename_from_utf8 (utf8, -1, NULL, NULL, error);
+
+      if (tmp)
+        g_free (tmp);
+      else
+        success = FALSE;
+
+      g_free (utf8);
+    }
+  else
+    success = FALSE;
+
+  if (success)
+    {
+      GFile *file = NULL;
+
+      if (filename && strlen (filename))
+        file = file_utils_filename_to_file (image->gimp, filename, NULL);
+
+      gimp_image_set_file (image, file);
+
+      if (file)
+        g_object_unref (file);
+    }
+}
+CODE
+    );
+}
+
+sub image_get_uri {
+    $blurb = "Returns the URI for the specified image.";
+
+    $help = <<'HELP';
+This procedure returns the URI associated with the specified image.
+The image has an URI only if it was loaded or imported from a file or
+has since been saved or exported. Otherwise, this function returns
+%NULL.  See also gimp-image-get-imported-uri to get the URI of the
+current file if it was imported from a non-GIMP file format and not
+yet saved, or gimp-image-get-exported-uri if the image has been
+exported to a non-GIMP file format.
+HELP
+
+    &neo_pdb_misc('2009', '2.8');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+    );
+
+    @outargs = (
+        { name => 'uri', type => 'string',
+          desc => 'The URI. The returned value must be freed with g_free()' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GFile *file = gimp_image_get_any_file (image);
+  if (file)
+    uri = g_file_get_uri (file);
+}
+CODE
+    );
+}
+
+sub image_get_xcf_uri {
+    $blurb = "Returns the XCF URI for the specified image.";
+
+    $help = <<'HELP';
+This procedure returns the XCF URI associated with the image. If
+there is no such URI, this procedure returns %NULL.
+HELP
+
+    $author = 'Eric Grivel <gimp lumenssolutions com>';
+    $copyright = 'Eric Grivel';
+    $date = '2011';
+    $since = '2.8';
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+    );
+
+    @outargs = (
+        { name => 'uri', type => 'string',
+          desc => 'The imported URI. The returned value must be freed with g_free()' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GFile *file = gimp_image_get_file (image);
+  if (file)
+    uri = g_file_get_uri (file);
+}
+CODE
+    );
+}
+
+sub image_get_imported_uri {
+    $blurb = "Returns the imported URI for the specified image.";
+
+    $help = <<'HELP';
+This procedure returns the URI associated with the specified image
+if the image was imported from a non-native Gimp format. If the
+image was not imported, or has since been saved in the native Gimp
+format, this procedure returns %NULL.
+HELP
+
+    $author = 'Eric Grivel <gimp lumenssolutions com>';
+    $copyright = 'Eric Grivel';
+    $date = '2011';
+    $since = '2.8';
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+    );
+
+    @outargs = (
+        { name => 'uri', type => 'string',
+          desc => 'The imported URI. The returned value must be freed with g_free()' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GFile *file = gimp_image_get_imported_file (image);
+  if (file)
+    uri = g_file_get_uri (file);
+}
+CODE
+    );
+}
+
+sub image_get_exported_uri {
+    $blurb = "Returns the exported URI for the specified image.";
+
+    $help = <<'HELP';
+This procedure returns the URI associated with the specified image
+if the image was exported a non-native GIMP format. If the
+image was not exported, this procedure returns %NULL.
+HELP
+
+    $author = 'Eric Grivel <gimp lumenssolutions com>';
+    $copyright = 'Eric Grivel';
+    $date = '2011';
+    $since = '2.8';
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+    );
+
+    @outargs = (
+        { name => 'uri', type => 'string',
+          desc => 'The exported URI. The returned value must be freed with g_free()' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GFile *file = gimp_image_get_exported_file (image);
+  if (file)
+    uri = g_file_get_uri (file);
+}
+CODE
+    );
+}
+
+sub image_get_name {
+    $blurb = "Returns the specified image's name.";
+    $help  =  <<'HELP';
+This procedure returns the image's name. If the image has a filename
+or an URI, then the returned name contains the filename's or URI's
+base name (the last component of the path). Otherwise it is the
+translated string "Untitled". The returned name is formatted like the
+image name in the image window title, it may contain '[]',
+'(imported)' etc. and should only be used to label user interface
+elements. Never use it to construct filenames.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+    );
+
+    @outargs = (
+        { name => 'name', type => 'string',
+          desc => 'The name. The returned value must be freed with g_free()' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  name = g_strdup (gimp_image_get_display_name (image));
+}
+CODE
+    );
+}
+
+sub image_get_resolution {
+    $blurb = "Returns the specified image's resolution.";
+
+    $help = <<'HELP';
+This procedure returns the specified image's resolution in dots per inch.
+This value is independent of any of the layers in this image.
+HELP
+
+    &austin_pdb_misc('1998');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+    );
+
+    @outargs = (
+        { name => 'xresolution', type => 'float', void_ret => 1,
+          desc => 'The resolution in the x-axis, in dots per inch' },
+        { name => 'yresolution', type => 'float',
+          desc => 'The resolution in the y-axis, in dots per inch' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  gimp_image_get_resolution (image, &xresolution, &yresolution);
+}
+CODE
+    );
+}
+
+sub image_set_resolution {
+    $blurb = "Sets the specified image's resolution.";
+
+    $help = <<'HELP';
+This procedure sets the specified image's resolution in dots per inch.
+This value is independent of any of the layers in this image.
+No scaling or resizing is performed.
+HELP
+
+    &austin_pdb_misc('1998');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'xresolution', type => 'float',
+          desc => 'The new image resolution in the x-axis, in dots per inch' },
+        { name => 'yresolution', type => 'float',
+          desc => 'The new image resolution in the y-axis, in dots per inch' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (! FINITE (xresolution) ||
+      xresolution < GIMP_MIN_RESOLUTION || xresolution > GIMP_MAX_RESOLUTION ||
+      ! FINITE (yresolution) ||
+      yresolution < GIMP_MIN_RESOLUTION || yresolution > GIMP_MAX_RESOLUTION)
+    {
+      g_set_error_literal (error, GIMP_PDB_ERROR,
+                           GIMP_PDB_ERROR_INVALID_ARGUMENT,
+                           _("Image resolution is out of bounds, "
+                             "using the default resolution instead."));
+      success = FALSE;
+    }
+  else
+    {
+      gimp_image_set_resolution (image, xresolution, yresolution);
+    }
+}
+CODE
+    );
+}
+
+sub image_get_unit {
+    $blurb = "Returns the specified image's unit.";
+
+    $help = <<'HELP';
+This procedure returns the specified image's unit. This value is
+independent of any of the layers in this image. See the gimp_unit_*()
+procedure definitions for the valid range of unit IDs and a
+description of the unit system.
+HELP
+
+    &mitch_pdb_misc('1998');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+    );
+
+    @outargs = (
+        { name => 'unit', type => 'unit',
+          desc => 'The unit' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  unit = gimp_image_get_unit (image);
+}
+CODE
+    );
+}
+
+sub image_set_unit {
+    $blurb = "Sets the specified image's unit.";
+
+    $help = <<'HELP';
+This procedure sets the specified image's unit. No scaling or resizing
+is performed. This value is independent of any of the layers in this
+image. See the gimp_unit_*() procedure definitions for the valid range
+of unit IDs and a description of the unit system.
+HELP
+
+    &mitch_pdb_misc('1998');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'unit', type => 'unit (min GIMP_UNIT_INCH)',
+          desc => 'The new image unit' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  gimp_image_set_unit (image, unit);
+}
+CODE
+    );
+}
+
+sub image_get_tattoo_state {
+    $blurb = 'Returns the tattoo state associated with the image.';
+
+    $help = <<'HELP';
+This procedure returns the tattoo state of the image. Use only by
+save/load plug-ins that wish to preserve an images tattoo state. Using this
+function at other times will produce unexpected results.
+HELP
+
+    &andy_pdb_misc('2000');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+    );
+
+    @outargs = (
+        { name => 'tattoo_state', type => 'tattoo',
+          desc => 'The tattoo state' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  tattoo_state = gimp_image_get_tattoo_state (image);
+}
+CODE
+    );
+}
+
+sub image_set_tattoo_state {
+    $blurb = 'Set the tattoo state associated with the image.';
+
+    $help = <<'HELP';
+This procedure sets the tattoo state of the image. Use only by
+save/load plug-ins that wish to preserve an images tattoo state. Using
+this function at other times will produce unexpected results. A full
+check of uniqueness of states in layers, channels and paths will be
+performed by this procedure and a execution failure will be returned
+if this fails. A failure will also be returned if the new tattoo state
+value is less than the maximum tattoo value from all of the tattoos
+from the paths, layers and channels. After the image data has been
+loaded and all the tattoos have been set then this is the last
+procedure that should be called. If effectively does a status check on
+the tattoo values that have been set to make sure that all is OK.
+HELP
+
+    &andy_pdb_misc('2000');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'tattoo_state', type => 'tattoo',
+          desc => 'The new image tattoo state' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  gimp_image_set_tattoo_state (image, tattoo_state);
+}
+CODE
+    );
+}
+
+sub image_get_layer_by_tattoo {
+    $blurb = 'Find a layer with a given tattoo in an image.';
+
+    $help = <<'HELP';
+This procedure returns the layer with the given tattoo in the specified image.
+HELP
+
+    &jay_pdb_misc('1998');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'tattoo', type => 'tattoo',
+          desc => 'The tattoo of the layer to find' }
+    );
+
+    @outargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The layer with the specified tattoo' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  layer = gimp_image_get_layer_by_tattoo (image, tattoo);
+}
+CODE
+    );
+}
+
+sub image_get_channel_by_tattoo {
+    $blurb = 'Find a channel with a given tattoo in an image.';
+
+    $help = <<'HELP';
+This procedure returns the channel with the given tattoo in the specified image.
+HELP
+
+    &jay_pdb_misc('1998');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'tattoo', type => 'tattoo',
+          desc => 'The tattoo of the channel to find' }
+    );
+
+    @outargs = (
+        { name => 'channel', type => 'channel',
+          desc => 'The channel with the specified tattoo' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  channel = gimp_image_get_channel_by_tattoo (image, tattoo);
+}
+CODE
+    );
+}
+
+sub image_get_vectors_by_tattoo {
+    $blurb = 'Find a vectors with a given tattoo in an image.';
+
+    $help = <<'HELP';
+This procedure returns the vectors with the given tattoo in the
+specified image.
+HELP
+
+    &simon_pdb_misc('2005', '2.6');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'tattoo', type => 'tattoo',
+          desc => 'The tattoo of the vectors to find' }
+    );
+
+    @outargs = (
+        { name => 'vectors', type => 'vectors',
+          desc => 'The vectors with the specified tattoo' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  vectors = gimp_image_get_vectors_by_tattoo (image, tattoo);
+}
+CODE
+    );
+}
+
+sub image_get_layer_by_name {
+    $blurb = 'Find a layer with a given name in an image.';
+
+    $help = <<'HELP';
+This procedure returns the layer with the given name in the specified image.
+HELP
+
+    &mitch_pdb_misc('2011', '2.8');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'name', type => 'string', non_empty => 1,
+          desc => 'The name of the layer to find' }
+    );
+
+    @outargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The layer with the specified name' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  layer = gimp_image_get_layer_by_name (image, name);
+}
+CODE
+    );
+}
+
+sub image_get_channel_by_name {
+    $blurb = 'Find a channel with a given name in an image.';
+
+    $help = <<'HELP';
+This procedure returns the channel with the given name in the specified image.
+HELP
+
+    &mitch_pdb_misc('2011', '2.8');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'name', type => 'string', non_empty => 1,
+          desc => 'The name of the channel to find' }
+    );
+
+    @outargs = (
+        { name => 'channel', type => 'channel',
+          desc => 'The channel with the specified name' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  channel = gimp_image_get_channel_by_name (image, name);
+}
+CODE
+    );
+}
+
+sub image_get_vectors_by_name {
+    $blurb = 'Find a vectors with a given name in an image.';
+
+    $help = <<'HELP';
+This procedure returns the vectors with the given name in the
+specified image.
+HELP
+
+    &mitch_pdb_misc('2011', '2.8');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'name', type => 'string', non_empty => 1,
+          desc => 'The name of the vectors to find' }
+    );
+
+    @outargs = (
+        { name => 'vectors', type => 'vectors',
+          desc => 'The vectors with the specified name' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  vectors = gimp_image_get_vectors_by_name (image, name);
+}
+CODE
+    );
+}
+
+sub image_attach_parasite {
+    $blurb = 'Add a parasite to an image.';
+
+    $help = <<'HELP';
+This procedure attaches a parasite to an image. It has no return values.
+HELP
+
+    &jay_pdb_misc('1998', '2.8');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'parasite', type => 'parasite',
+          desc => 'The parasite to attach to an image' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_image_parasite_validate (image, parasite, error))
+    gimp_image_parasite_attach (image, parasite);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_detach_parasite {
+    $blurb = 'Removes a parasite from an image.';
+
+    $help = <<'HELP';
+This procedure detaches a parasite from an image. It has no return values.
+HELP
+
+    &jay_pdb_misc('1998', '2.8');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'name', type => 'string',
+          desc => 'The name of the parasite to detach from an image.' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  gimp_image_parasite_detach (image, name);
+}
+CODE
+    );
+}
+
+sub image_get_parasite {
+    $blurb = 'Look up a parasite in an image';
+
+    $help = <<'HELP';
+Finds and returns the parasite that was previously attached to an image.
+HELP
+
+    &jay_pdb_misc('1998', '2.8');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'name', type => 'string',
+          desc => 'The name of the parasite to find' }
+    );
+
+    @outargs = (
+        { name => 'parasite', type => 'parasite',
+          desc => 'The found parasite' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  parasite = gimp_parasite_copy (gimp_image_parasite_find (image, name));
+
+  if (! parasite)
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_get_parasite_list {
+    $blurb = 'List all parasites.';
+    $help  = 'Returns a list of all currently attached parasites.';
+
+    &marc_pdb_misc('1999', '2.8');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+    );
+
+    @outargs = (
+        { name => 'parasites', type => 'stringarray',
+          desc => 'The names of currently attached parasites',
+          array => { desc => 'The number of attached parasites' } }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  parasites = gimp_image_parasite_list (image, &num_parasites);
+}
+CODE
+    );
+}
+
+sub image_thumbnail {
+    $blurb = 'Get a thumbnail of an image.';
+
+    $help = <<'HELP';
+This function gets data from which a thumbnail of an image preview can
+be created. Maximum x or y dimension is 1024 pixels. The pixels are
+returned in RGB[A] or GRAY[A] format. The bpp return value gives the
+number of bits per pixel in the image.
+HELP
+
+    &andy_pdb_misc('1999');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'width', type => '1 <= int32 <= 1024',
+          desc => 'The requested thumbnail width' },
+        { name => 'height', type => '1 <= int32 <= 1024',
+          desc => 'The requested thumbnail height' }
+    );
+
+    @outargs = (
+        { name => 'actual_width', type => 'int32', void_ret => 1,
+          desc => 'The previews width' },
+        { name => 'actual_height', type => 'int32',
+          desc => 'The previews height' },
+        { name => 'bpp', type => 'int32',
+          desc => 'The previews bpp' },
+        { name => 'thumbnail_data', type => 'int8array', wrap => 1,
+          desc => 'The thumbnail data',
+          array => { name => 'thumbnail_data_count',
+                     desc => 'The number of bytes in thumbnail data' } }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpTempBuf *buf;
+  gint         dwidth, dheight;
+
+  g_assert (GIMP_VIEWABLE_MAX_PREVIEW_SIZE >= 1024);
+
+  /* Adjust the width/height ratio */
+  dwidth  = gimp_image_get_width  (image);
+  dheight = gimp_image_get_height (image);
+
+  if (dwidth > dheight)
+    height = MAX (1, (width * dheight) / dwidth);
+  else
+    width  = MAX (1, (height * dwidth) / dheight);
+
+  gimp_pickable_flush (GIMP_PICKABLE (image));
+
+  buf = gimp_viewable_get_new_preview (GIMP_VIEWABLE (image), context,
+                                       width, height);
+
+  if (buf)
+    {
+      actual_width         = gimp_temp_buf_get_width  (buf);
+      actual_height        = gimp_temp_buf_get_height (buf);
+      bpp                  = babl_format_get_bytes_per_pixel (gimp_temp_buf_get_format (buf));
+      thumbnail_data_count = gimp_temp_buf_get_data_size (buf);
+      thumbnail_data       = g_memdup (gimp_temp_buf_get_data (buf),
+                                       thumbnail_data_count);
+
+      gimp_temp_buf_unref (buf);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+$extra{app}->{code} = <<'CODE';
+#if defined (HAVE_FINITE)
+#define FINITE(x) finite(x)
+#elif defined (HAVE_ISFINITE)
+#define FINITE(x) isfinite(x)
+#elif defined (G_OS_WIN32)
+#define FINITE(x) _finite(x)
+#else
+#error "no FINITE() implementation available?!"
+#endif
+CODE
+
+@headers = qw("libgimpmath/gimpmath.h"
+              "libgimpbase/gimpbase.h"
+              "core/gimp.h"
+              "core/gimpcontainer.h"
+              "core/gimpimage-metadata.h"
+              "core/gimpprogress.h"
+              "core/gimptempbuf.h"
+              "file/file-utils.h"
+              "plug-in/gimpplugin.h"
+              "plug-in/gimppluginmanager.h"
+              "gimppdbcontext.h"
+              "gimppdberror.h"
+              "gimppdb-utils.h"
+              "gimp-intl.h");
+
+@procs = qw(image_is_valid
+            image_list
+            image_new image_new_with_precision
+            image_duplicate image_delete
+            image_base_type
+            image_get_precision
+            image_get_default_new_layer_mode
+            image_width image_height
+            image_free_shadow
+            image_get_layers
+            image_get_channels
+            image_get_vectors
+            image_get_active_drawable
+            image_unset_active_channel
+            image_get_floating_sel
+            image_floating_sel_attached_to
+            image_pick_color
+            image_pick_correlate_layer
+            image_add_layer image_insert_layer image_remove_layer
+            image_add_channel image_insert_channel image_remove_channel
+            image_add_vectors image_insert_vectors image_remove_vectors
+            image_get_item_position
+            image_raise_item image_lower_item
+            image_raise_item_to_top image_lower_item_to_bottom
+            image_reorder_item
+            image_flatten image_merge_visible_layers image_merge_down
+            image_add_layer_mask image_remove_layer_mask
+            image_get_colormap image_set_colormap
+            image_get_metadata image_set_metadata
+            image_clean_all image_is_dirty
+            image_thumbnail
+            image_get_active_layer image_set_active_layer
+            image_get_active_channel image_set_active_channel
+            image_get_active_vectors image_set_active_vectors
+            image_get_selection
+            image_get_component_active image_set_component_active
+            image_get_component_visible image_set_component_visible
+            image_get_filename image_set_filename
+            image_get_uri
+            image_get_xcf_uri
+            image_get_imported_uri
+            image_get_exported_uri
+            image_get_name
+            image_get_resolution image_set_resolution
+            image_get_unit image_set_unit
+            image_get_tattoo_state image_set_tattoo_state
+            image_get_layer_by_tattoo
+            image_get_channel_by_tattoo
+            image_get_vectors_by_tattoo
+            image_get_layer_by_name
+            image_get_channel_by_name
+            image_get_vectors_by_name
+            image_attach_parasite image_detach_parasite
+            image_get_parasite
+            image_get_parasite_list);
+
+# For the lib parameter EXCLUDE functions #43 and #44, which are
+# image_add_layer_mask and image_remove_layer_mask.
+# If adding or removing functions, make sure the range below is
+# updated correctly!
+%exports = (app => [@procs], lib => [@procs[0..38,41..81]]);
+
+$desc = 'Image';
+$doc_title = 'gimpimage';
+$doc_short_desc = 'Operations on complete images.';
+$doc_long_desc = 'Operations on complete images: creation, resizing/rescaling, and operations involving 
multiple layers.';
+
+1;
diff --git a/pdb/groups/image_color_profile.pdb b/pdb/groups/image_color_profile.pdb
new file mode 100644
index 0000000..cdef1ed
--- /dev/null
+++ b/pdb/groups/image_color_profile.pdb
@@ -0,0 +1,327 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub image_get_color_profile {
+    $blurb = "Returns the image's color profile";
+
+    $help = <<'HELP';
+This procedure returns the image's color profile, or NULL if the image
+has no color profile assigned.
+HELP
+
+    &mitch_pdb_misc('2015', '2.10');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+    );
+
+    @outargs = (
+        { name => 'profile_data', type => 'int8array', wrap => 1,
+          desc => "The image's serialized color profile. The returned value must be freed with g_free()",
+          array => { name => 'num_bytes',
+                     desc => 'Number of bytes in the color_profile array' } }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpColorProfile *profile;
+
+  profile = gimp_image_get_color_profile (image);
+
+  if (profile)
+    {
+      const guint8 *data;
+      gsize         length;
+
+      data = gimp_color_profile_get_icc_profile (profile, &length);
+
+      profile_data = g_memdup (data, length);
+      num_bytes = length;
+    }
+}
+CODE
+    );
+}
+
+sub image_get_effective_color_profile {
+    $blurb = "Returns the color profile that is used for the image";
+
+    $help = <<'HELP';
+This procedure returns the color profile that is actually used for
+this image, which is the profile returned by
+gimp_image_get_color_profile() if the image has a profile assigned, or
+a generated default RGB or grayscale profile, according to the image's type.
+HELP
+
+    &mitch_pdb_misc('2015', '2.10');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+    );
+
+    @outargs = (
+        { name => 'profile_data', type => 'int8array', wrap => 1,
+          desc => "The image's serialized color profile. The returned value must be freed with g_free()",
+          array => { name => 'num_bytes',
+                     desc => 'Number of bytes in the color_profile array' } }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpColorProfile *profile;
+
+  profile = gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (image));
+
+  if (profile)
+    {
+      const guint8 *data;
+      gsize         length;
+
+      data = gimp_color_profile_get_icc_profile (profile, &length);
+
+      profile_data = g_memdup (data, length);
+      num_bytes = length;
+    }
+}
+CODE
+    );
+}
+
+sub image_set_color_profile {
+    $blurb = "Sets the image's color profile";
+
+    $help = <<'HELP';
+This procedure sets the image's color profile, or unsets it if NULL is
+passed as 'color_profile'. This procedure does no color conversion.
+HELP
+
+    &mitch_pdb_misc('2015', '2.10');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'color_profile', type => 'int8array', wrap => 1,
+          desc => 'The new serialized color profile',
+          array => { name => 'num_bytes',
+                     desc => 'Number of bytes in the color_profile array' } }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (color_profile)
+    {
+      GimpColorProfile *profile;
+
+      profile = gimp_color_profile_new_from_icc_profile (color_profile,
+                                                         num_bytes,
+                                                         error);
+
+      if (profile)
+        {
+          success = gimp_image_set_color_profile (image, profile, error);
+          g_object_unref (profile);
+        }
+      else
+        success = FALSE;
+    }
+  else
+    {
+      success = gimp_image_set_color_profile (image, NULL, error);
+    }
+}
+CODE
+    );
+}
+
+sub image_set_color_profile_from_file {
+    $blurb = "Sets the image's color profile from an ICC file";
+
+    $help = <<'HELP';
+This procedure sets the image's color profile from a file containing
+an ICC profile, or unsets it if NULL is passed as 'uri'. This
+procedure does no color conversion.
+HELP
+
+    &mitch_pdb_misc('2015', '2.10');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'uri', type => 'string',
+          desc => 'The URI of the file containing the new color profile' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (uri)
+    {
+      GFile            *file = g_file_new_for_uri (uri);
+      GimpColorProfile *profile;
+
+      profile = gimp_color_profile_new_from_file (file, error);
+
+      if (profile)
+        {
+          success = gimp_image_set_color_profile (image, profile, error);
+          g_object_unref (profile);
+        }
+      else
+        success = FALSE;
+
+      g_object_unref (file);
+    }
+  else
+    {
+      success = gimp_image_set_color_profile (image, NULL, error);
+    }
+}
+CODE
+    );
+}
+
+sub image_convert_color_profile {
+    $blurb = "Convert the image's layers to a color profile";
+
+    $help = <<'HELP';
+This procedure converts from the image's color profile (or the default
+RGB or grayscale profile if none is set) to the given color profile. Only
+RGB and grayscale color profiles are accepted, according to the image's
+type.
+HELP
+
+    &mitch_pdb_misc('2015', '2.10');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'color_profile', type => 'int8array', wrap => 1,
+          desc => 'The serialized color profile',
+          array => { name => 'num_bytes',
+                     desc => 'Number of bytes in the color_profile array' } },
+        { name => 'intent', type => 'enum GimpColorRenderingIntent',
+          desc => 'Rendering intent' },
+        { name => 'bpc', type => 'boolean',
+          desc => 'Black point compensation' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (color_profile)
+    {
+      GimpColorProfile *profile;
+
+      profile = gimp_color_profile_new_from_icc_profile (color_profile,
+                                                         num_bytes,
+                                                         error);
+
+      if (profile)
+        {
+          success = gimp_image_convert_color_profile (image, profile,
+                                                      intent, bpc,
+                                                      progress, error);
+          g_object_unref (profile);
+        }
+      else
+        success = FALSE;
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_convert_color_profile_from_file {
+    $blurb = "Convert the image's layers to a color profile";
+
+    $help = <<'HELP';
+This procedure converts from the image's color profile (or the default
+RGB or grayscale profile if none is set) to an ICC profile specified by
+'uri'.  Only RGB and grayscale color profiles are accepted, according to
+the image's type.
+HELP
+
+    &mitch_pdb_misc('2015', '2.10');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'uri', type => 'string',
+          desc => 'The URI of the file containing the new color profile' },
+        { name => 'intent', type => 'enum GimpColorRenderingIntent',
+          desc => 'Rendering intent' },
+        { name => 'bpc', type => 'boolean',
+          desc => 'Black point compensation' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (uri)
+    {
+      GFile            *file = g_file_new_for_uri (uri);
+      GimpColorProfile *profile;
+
+      profile = gimp_color_profile_new_from_file (file, error);
+
+      if (profile)
+        {
+          success = gimp_image_convert_color_profile (image, profile,
+                                                      intent, bpc,
+                                                      progress, error);
+          g_object_unref (profile);
+        }
+      else
+        success = FALSE;
+
+      g_object_unref (file);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+@headers = qw(<cairo.h>
+              "libgimpcolor/gimpcolor.h"
+              "core/gimpimage-color-profile.h"
+              "gimp-intl.h");
+
+@procs = qw(image_get_color_profile
+            image_get_effective_color_profile
+            image_set_color_profile
+            image_set_color_profile_from_file
+            image_convert_color_profile
+            image_convert_color_profile_from_file);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Image Color Profile';
+$doc_title = 'gimpimagecolorprofile';
+$doc_short_desc = 'Operations on an image\'s color profile.';
+$doc_long_desc = 'Operations on an image\'s color profile.';
+
+1;
diff --git a/pdb/groups/image_convert.pdb b/pdb/groups/image_convert.pdb
new file mode 100644
index 0000000..e65dfa9
--- /dev/null
+++ b/pdb/groups/image_convert.pdb
@@ -0,0 +1,286 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub image_convert_rgb {
+    $blurb = 'Convert specified image to RGB color';
+
+    $help = <<'HELP';
+This procedure converts the specified image to RGB color. This process
+requires an image in Grayscale or Indexed color mode. No image content is
+lost in this process aside from the colormap for an indexed image.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_image_is_not_base_type (image, GIMP_RGB, error))
+    {
+      success = gimp_image_convert_type (image, GIMP_RGB, NULL, NULL, error);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub image_convert_grayscale {
+    $blurb = 'Convert specified image to grayscale';
+
+    $help = <<'HELP';
+This procedure converts the specified image to grayscale. This process
+requires an image in RGB or Indexed color mode.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_image_is_not_base_type (image, GIMP_GRAY, error))
+    {
+      success = gimp_image_convert_type (image, GIMP_GRAY, NULL, NULL, error);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub image_convert_indexed {
+    $blurb = 'Convert specified image to and Indexed image';
+
+    $help = <<'HELP';
+This procedure converts the specified image to 'indexed' color. This
+process requires an image in RGB or Grayscale mode. The 'palette_type'
+specifies what kind of palette to use, A type of '0' means to use an
+optimal palette of 'num_cols' generated from the colors in the
+image. A type of '1' means to re-use the previous palette (not
+currently implemented). A type of '2' means to use the so-called
+WWW-optimized palette. Type '3' means to use only black and white
+colors. A type of '4' means to use a palette from the gimp palettes
+directories.  The 'dither type' specifies what kind of dithering to
+use.  '0' means no dithering, '1' means standard Floyd-Steinberg error
+diffusion, '2' means Floyd-Steinberg error diffusion with reduced
+bleeding, '3' means dithering based on pixel location ('Fixed'
+dithering).
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+       { name => 'dither_type', type => 'enum GimpConvertDitherType',
+         desc => 'The dither type to use' },
+       { name => 'palette_type', type => 'enum GimpConvertPaletteType',
+         desc => 'The type of palette to use' },
+       { name => 'num_cols', type => 'int32',
+         desc => 'The number of colors to quantize to, ignored unless
+                  (palette_type == GIMP_CONVERT_PALETTE_GENERATE)' },
+       { name => 'alpha_dither', type => 'boolean',
+         desc => 'Dither transparency to fake partial opacity' },
+       { name => 'remove_unused', type => 'boolean',
+         desc => 'Remove unused or duplicate color entries from final
+                  palette, ignored if (palette_type ==
+                  GIMP_CONVERT_PALETTE_GENERATE)' },
+       { name => 'palette', type => 'string',
+         desc => 'The name of the custom palette to use, ignored unless
+                  (palette_type == GIMP_CONVERT_PALETTE_CUSTOM)' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPalette *pal = NULL;
+
+  if (gimp_pdb_image_is_not_base_type (image, GIMP_INDEXED, error) &&
+      gimp_pdb_image_is_precision (image, GIMP_PRECISION_U8_GAMMA, error) &&
+      gimp_item_stack_is_flat (GIMP_ITEM_STACK (gimp_image_get_layers (image))))
+    {
+      switch (palette_type)
+       {
+       case GIMP_CONVERT_PALETTE_GENERATE:
+         if (num_cols < 1 || num_cols > MAXNUMCOLORS)
+           success = FALSE;
+         break;
+
+       case GIMP_CONVERT_PALETTE_CUSTOM:
+          pal = gimp_pdb_get_palette (gimp, palette, FALSE, error);
+         if (! pal)
+            {
+              success = FALSE;
+            }
+          else if (pal->n_colors > MAXNUMCOLORS)
+            {
+              g_set_error_literal (error,
+                                  GIMP_PDB_ERROR,
+                                   GIMP_PDB_ERROR_INVALID_ARGUMENT,
+                                   _("Cannot convert to a palette "
+                                     "with more than 256 colors."));
+              success = FALSE;
+            }
+          break;
+
+       default:
+          break;
+       }
+    }
+  else
+    {
+      success = FALSE;
+    }
+
+  if (success)
+    success = gimp_image_convert_indexed (image,
+                                          palette_type, num_cols, remove_unused,
+                                          dither_type, alpha_dither, FALSE,
+                                          pal,
+                                          NULL, error);
+}
+CODE
+    );
+}
+
+sub image_convert_set_dither_matrix {
+    $blurb = 'Set dither matrix for conversion to indexed';
+
+    $help = <<'HELP';
+This procedure sets the dither matrix used when converting images to INDEXED mode with 
+positional dithering. 
+HELP
+
+    &david_pdb_misc('2006', '2.4');
+
+    @inargs = (
+       { name => 'width', type => 'int32',
+         desc => 'Width of the matrix (0 to reset to default matrix)' },
+       { name => 'height', type => 'int32',
+         desc => 'Height of the matrix (0 to reset to default matrix)' },
+       { name => 'matrix', type => 'int8array',
+         desc => 'The matrix -- all values must be >= 1',
+         array => { name => 'matrix_length', type => '1 <= int32 <= 1024',
+                     desc => "The length of 'matrix'" }
+         },
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (width == 0 || height == 0 || matrix_length == width * height)
+    {
+      gimp_image_convert_indexed_set_dither_matrix (matrix, width, height);
+    }
+  else
+    {
+      g_set_error_literal (error, GIMP_PDB_ERROR,
+                           GIMP_PDB_ERROR_INVALID_ARGUMENT,
+                           "Dither matrix length must be width * height");
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub image_convert_precision {
+    $blurb = 'Convert the image to the specified precision';
+
+    $help = <<'HELP';
+This procedure converts the image to the specified precision. Note
+that indexed images cannot be converted and are always in
+GIMP_PRECISION_U8.
+HELP
+
+    &mitch_pdb_misc('2012', '2.10');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+       { name => 'precision', type => 'enum GimpPrecision',
+         desc => 'The new precision' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp->plug_in_manager->current_plug_in)
+    gimp_plug_in_enable_precision (gimp->plug_in_manager->current_plug_in);
+
+  if (gimp_pdb_image_is_not_base_type (image, GIMP_INDEXED, error) &&
+      gimp_pdb_image_is_not_precision (image, precision, error))
+    {
+      gimp_image_convert_precision (image, precision,
+                                    GEGL_DITHER_NONE,
+                                    GEGL_DITHER_NONE,
+                                    GEGL_DITHER_NONE,
+                                    progress);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+@headers = qw("core/gimp.h"
+              "core/gimpimage.h"
+              "core/gimpimage-convert-indexed.h"
+              "core/gimpimage-convert-precision.h"
+              "core/gimpimage-convert-type.h"
+              "core/gimpitemstack.h"
+              "core/gimppalette.h"
+              "plug-in/gimpplugin.h"
+              "plug-in/gimppluginmanager.h"
+              "gimppdberror.h"
+              "gimppdb-utils.h"
+              "gimp-intl.h");
+
+@procs = qw(image_convert_rgb
+            image_convert_grayscale
+            image_convert_indexed
+            image_convert_set_dither_matrix
+            image_convert_precision);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Image Convert';
+$doc_title = 'gimpimageconvert';
+$doc_short_desc = 'Conversions between RGB, indexed, and grayscale modes.';
+$doc_long_desc = 'Conversions between RGB, indexed, and grayscale modes.';
+
+1;
diff --git a/pdb/groups/image_grid.pdb b/pdb/groups/image_grid.pdb
new file mode 100644
index 0000000..f1ae3d1
--- /dev/null
+++ b/pdb/groups/image_grid.pdb
@@ -0,0 +1,372 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+sub image_grid_get_spacing {
+    $blurb = "Gets the spacing of an image's grid.";
+
+    $help = <<HELP;
+This procedure retrieves the horizontal and vertical spacing of an image's grid.
+It takes the image as parameter.
+HELP
+
+    &sylvain_pdb_misc('2005', '2.4');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' }
+    );
+
+    @outargs = (
+        { name => 'xspacing', type => 'float', void_ret => 1,
+          desc => "The image's grid horizontal spacing" },
+        { name => 'yspacing', type => 'float', void_ret => 1,
+          desc => "The image's grid vertical spacing" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpGrid *grid = gimp_image_get_grid (image);
+
+  if (grid)
+    g_object_get (grid,
+                  "xspacing", &xspacing,
+                  "yspacing", &yspacing,
+                  NULL);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_grid_set_spacing {
+    $blurb = "Sets the spacing of an image's grid.";
+
+    $help = <<HELP;
+This procedure sets the horizontal and vertical spacing of an image's grid.
+HELP
+
+    &sylvain_pdb_misc('2005', '2.4');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+        { name => 'xspacing', type => 'float',
+          desc => "The image's grid horizontal spacing" },
+        { name => 'yspacing', type => 'float',
+          desc => "The image's grid vertical spacing" }
+    );
+
+    %invoke = (
+       code => <<CODE
+{
+  GimpGrid *grid = gimp_image_get_grid (image);
+
+  if (grid)
+    g_object_set (grid,
+                  "xspacing", xspacing,
+                  "yspacing", yspacing,
+                  NULL);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_grid_get_offset {
+    $blurb = "Gets the offset of an image's grid.";
+
+    $help = <<HELP;
+This procedure retrieves the horizontal and vertical offset of an image's grid.
+It takes the image as parameter.
+HELP
+
+    &sylvain_pdb_misc('2005', '2.4');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' }
+    );
+
+    @outargs = (
+        { name => 'xoffset', type => 'float', void_ret => 1,
+          desc => "The image's grid horizontal offset" },
+        { name => 'yoffset', type => 'float', void_ret => 1,
+          desc => "The image's grid vertical offset" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpGrid *grid = gimp_image_get_grid (image);
+
+  if (grid)
+    g_object_get (grid,
+                  "xoffset", &xoffset,
+                  "yoffset", &yoffset,
+                  NULL);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_grid_set_offset {
+    $blurb = "Sets the offset of an image's grid.";
+
+    $help = <<HELP;
+This procedure sets the horizontal and vertical offset of an image's grid.
+HELP
+
+    &sylvain_pdb_misc('2005', '2.4');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+        { name => 'xoffset', type => 'float',
+          desc => "The image's grid horizontal offset" },
+        { name => 'yoffset', type => 'float',
+          desc => "The image's grid vertical offset" }
+    );
+
+    %invoke = (
+       code => <<CODE
+{
+  GimpGrid *grid = gimp_image_get_grid (image);
+
+  if (grid)
+    g_object_set (grid,
+                  "xoffset", xoffset,
+                  "yoffset", yoffset,
+                  NULL);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_grid_get_foreground_color {
+    $blurb = "Sets the foreground color of an image's grid.";
+
+    $help = <<HELP;
+This procedure gets the foreground color of an image's grid.
+HELP
+
+    &sylvain_pdb_misc('2005', '2.4');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' }
+    );
+
+    @outargs = (
+        { name => 'fgcolor', type => 'color', has_alpha => 1, void_ret => 1,
+          desc => "The image's grid foreground color" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpGrid *grid = gimp_image_get_grid (image);
+
+  if (grid)
+    fgcolor = grid->fgcolor;
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_grid_set_foreground_color {
+    $blurb = "Gets the foreground color of an image's grid.";
+
+    $help = <<HELP;
+This procedure sets the foreground color of an image's grid.
+HELP
+
+    &sylvain_pdb_misc('2005', '2.4');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+        { name => 'fgcolor', type => 'color', has_alpha => 1,
+          desc => "The new foreground color" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpGrid *grid = gimp_image_get_grid (image);
+
+  if (grid)
+    g_object_set (grid, "fgcolor", &fgcolor, NULL);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_grid_get_background_color {
+    $blurb = "Sets the background color of an image's grid.";
+
+    $help = <<HELP;
+This procedure gets the background color of an image's grid.
+HELP
+
+    &sylvain_pdb_misc('2005', '2.4');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' }
+    );
+
+    @outargs = (
+        { name => 'bgcolor', type => 'color', has_alpha => 1, void_ret => 1,
+          desc => "The image's grid background color" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpGrid *grid = gimp_image_get_grid (image);
+
+  if (grid)
+    bgcolor = grid->bgcolor;
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_grid_set_background_color {
+    $blurb = "Gets the background color of an image's grid.";
+
+    $help = <<HELP;
+This procedure sets the background color of an image's grid.
+HELP
+
+    &sylvain_pdb_misc('2005', '2.4');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+        { name => 'bgcolor', type => 'color', has_alpha => 1,
+          desc => "The new background color" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpGrid *grid = gimp_image_get_grid (image);
+
+  if (grid)
+    g_object_set (grid, "bgcolor", &bgcolor, NULL);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_grid_get_style {
+    $blurb = "Gets the style of an image's grid.";
+
+    $help = <<HELP;
+This procedure retrieves the style of an image's grid.
+HELP
+
+    &sylvain_pdb_misc('2005', '2.4');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' }
+    );
+
+    @outargs = (
+        { name => 'style', type => 'enum GimpGridStyle',
+          desc => "The image's grid style" }
+    );
+
+    %invoke = (
+       code => <<CODE
+{
+  GimpGrid *grid = gimp_image_get_grid (image);
+
+  if (grid)
+    g_object_get (grid, "style", &style, NULL);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_grid_set_style {
+    $blurb = "Sets the style unit of an image's grid.";
+
+    $help = <<HELP;
+This procedure sets the style of an image's grid.
+It takes the image and the new style as parameters.
+HELP
+
+    &sylvain_pdb_misc('2005', '2.4');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+        { name => 'style', type => 'enum GimpGridStyle',
+          desc => "The image's grid style" }
+    );
+
+    %invoke = (
+       code => <<CODE
+{
+  GimpGrid *grid = gimp_image_get_grid (image);
+
+  if (grid)
+    g_object_set (grid, "style", style, NULL);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+@headers = qw("core/gimpimage-grid.h" "core/gimpgrid.h"
+              "libgimpbase/gimpbaseenums.h");
+
+@procs = qw(image_grid_get_spacing image_grid_set_spacing
+            image_grid_get_offset image_grid_set_offset
+            image_grid_get_foreground_color image_grid_set_foreground_color
+            image_grid_get_background_color image_grid_set_background_color
+            image_grid_get_style image_grid_set_style);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Image grid procedures';
+$doc_title = 'gimpimagegrid';
+$doc_short_desc = "Functions manuipulating an image's grid.";
+$doc_long_desc = "Functions manuipulating an image's grid.";
+
+1;
diff --git a/pdb/groups/image_guides.pdb b/pdb/groups/image_guides.pdb
new file mode 100644
index 0000000..88e0c49
--- /dev/null
+++ b/pdb/groups/image_guides.pdb
@@ -0,0 +1,270 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub image_add_hguide {
+    $blurb = 'Add a horizontal guide to an image.';
+
+    $help = <<HELP;
+This procedure adds a horizontal guide to an image. It takes the input
+image and the y-position of the new guide as parameters. It returns
+the guide ID of the new guide.
+HELP
+
+    &adam_pdb_misc('1998');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+       { name => 'yposition', type => '0 <= int32',
+         desc => "The guide's y-offset from top of image" }
+    );
+
+    @outargs = (
+       { name => 'guide', type => 'guide',
+         desc => 'The new guide' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (yposition <= gimp_image_get_height (image))
+    {
+      GimpGuide *g;
+
+      g = gimp_image_add_hguide (image, yposition, TRUE);
+      guide = gimp_guide_get_ID (g);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_add_vguide {
+    $blurb = 'Add a vertical guide to an image.';
+
+    $help = <<HELP;
+This procedure adds a vertical guide to an image. It takes the input
+image and the x-position of the new guide as parameters. It returns
+the guide ID of the new guide.
+HELP
+
+    &adam_pdb_misc('1998');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+       { name => 'xposition', type => '0 <= int32',
+         desc => "The guide's x-offset from left of image" }
+    );
+
+    @outargs = (
+       { name => 'guide', type => 'guide',
+         desc => 'The new guide' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (xposition <= gimp_image_get_width (image))
+    {
+      GimpGuide *g;
+
+      g = gimp_image_add_vguide (image, xposition, TRUE);
+      guide = gimp_guide_get_ID (g);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_delete_guide {
+    $blurb = 'Deletes a guide from an image.';
+
+    $help = <<'HELP';
+This procedure takes an image and a guide ID as input and removes the specified
+guide from the specified image.
+HELP
+
+    &adam_pdb_misc('1998');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+       { name => 'guide', type => 'guide',
+         desc => 'The ID of the guide to be removed' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpGuide *g = gimp_pdb_image_get_guide (image, guide, error);
+
+  if (g)
+    gimp_image_remove_guide (image, g, TRUE);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_find_next_guide {
+    $blurb = 'Find next guide on an image.';
+
+    $help = <<'HELP';
+This procedure takes an image and a guide ID as input and finds the guide ID of
+the successor of the given guide ID in the image's guide list. If the supplied
+guide ID is 0, the procedure will return the first Guide. The procedure will
+return 0 if given the final guide ID as an argument or the image has no guides.
+HELP
+
+    &adam_pdb_misc('1998');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+       { name => 'guide', type => 'guide', no_validate => 1,
+         desc => 'The ID of the current guide (0 if first invocation)' }
+    );
+
+    @outargs = (
+       { name => 'next_guide', type => 'guide',
+         desc => "The next guide's ID" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpGuide *g = gimp_image_get_next_guide (image, guide, &success);
+
+  if (g)
+    next_guide = gimp_guide_get_ID (g);
+
+  if (! success)
+    g_set_error (error, GIMP_PDB_ERROR, GIMP_PDB_ERROR_INVALID_ARGUMENT,
+                 _("Image '%s' (%d) does not contain guide with ID %d"),
+                 gimp_image_get_display_name (image),
+                 gimp_image_get_ID (image),
+                 guide);
+}
+CODE
+    );
+}
+
+sub image_get_guide_orientation {
+    $blurb = 'Get orientation of a guide on an image.';
+
+    $help = <<'HELP';
+This procedure takes an image and a guide ID as input and returns the
+orientations of the guide.
+HELP
+
+    &adam_pdb_misc('1998');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+       { name => 'guide', type => 'guide',
+         desc => 'The guide' }
+    );
+
+    @outargs = (
+       { name => 'orientation',
+         type => 'enum GimpOrientationType (no GIMP_ORIENTATION_UNKNOWN)',
+         desc => "The guide's orientation",
+         libdef => 'GIMP_ORIENTATION_UNKNOWN' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpGuide *g = gimp_pdb_image_get_guide (image, guide, error);
+
+  if (g)
+    orientation = gimp_guide_get_orientation (g);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_get_guide_position {
+    $blurb = 'Get position of a guide on an image.';
+
+    $help = <<'HELP';
+This procedure takes an image and a guide ID as input and returns the position
+of the guide relative to the top or left of the image.
+HELP
+
+    &adam_pdb_misc('1998');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+       { name => 'guide', type => 'guide',
+         desc => 'The guide' }
+    );
+
+    @outargs = (
+       { name => 'position', type => 'int32',
+          libdef => 'G_MININT /* GIMP_GUIDE_POSITION_UNDEFINED */',
+         desc => "The guide's position relative to top or left of image" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpGuide *g = gimp_pdb_image_get_guide (image, guide, error);
+
+  if (g)
+    position = gimp_guide_get_position (g);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+@headers = qw("cairo.h"
+              "core/gimpguide.h"
+              "core/gimpimage-guides.h"
+              "core/gimpimage-undo-push.h"
+              "gimppdb-utils.h"
+              "gimppdberror.h"
+              "gimp-intl.h");
+
+@procs = qw(image_add_hguide image_add_vguide
+            image_delete_guide
+            image_find_next_guide
+            image_get_guide_orientation
+           image_get_guide_position);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Image Guide procedures';
+$doc_title = 'gimpimageguides';
+$doc_short_desc = 'Functions for manipulating an image\'s guides.';
+$doc_long_desc = 'Functions for manipulating an image\'s guides.';
+
+1;
diff --git a/pdb/groups/image_sample_points.pdb b/pdb/groups/image_sample_points.pdb
new file mode 100644
index 0000000..01b6669
--- /dev/null
+++ b/pdb/groups/image_sample_points.pdb
@@ -0,0 +1,202 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub image_add_sample_point {
+    $blurb = 'Add a sample point to an image.';
+
+    $help = <<HELP;
+This procedure adds a sample point to an image. It takes the input
+image and the position of the new sample points as parameters. It
+returns the sample point ID of the new sample point.
+HELP
+
+    &mitch_pdb_misc('2016', '2.10');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+       { name => 'position_x', type => '0 <= int32',
+         desc => "The guide'sample points x-offset from left of image" },
+       { name => 'position_y', type => '0 <= int32',
+         desc => "The guide'sample points y-offset from top of image" }
+    );
+
+    @outargs = (
+       { name => 'sample_point', type => 'sample_point',
+         desc => 'The new sample point' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (position_x <= gimp_image_get_width  (image) &&
+      position_y <= gimp_image_get_height (image))
+    {
+      GimpSamplePoint *sp;
+
+      sp = gimp_image_add_sample_point_at_pos (image, position_x, position_y,
+                                               TRUE);
+      sample_point = gimp_sample_point_get_ID (sp);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_delete_sample_point {
+    $blurb = 'Deletes a sample point from an image.';
+
+    $help = <<'HELP';
+This procedure takes an image and a sample point ID as input and
+removes the specified sample point from the specified image.
+HELP
+
+    &mitch_pdb_misc('2016', '2.10');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+       { name => 'sample_point', type => 'sample_point',
+         desc => 'The ID of the sample point to be removed' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpSamplePoint *sp = gimp_pdb_image_get_sample_point (image, sample_point,
+                                                         error);
+
+  if (sp)
+    gimp_image_remove_sample_point (image, sp, TRUE);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub image_find_next_sample_point {
+    $blurb = 'Find next sample point on an image.';
+
+    $help = <<'HELP';
+This procedure takes an image and a sample point ID as input and finds
+the sample point ID of the successor of the given sample point ID in
+the image's sample point list. If the supplied sample point ID is 0,
+the procedure will return the first sample point. The procedure will
+return 0 if given the final sample point ID as an argument or the
+image has no sample points.
+HELP
+
+    &mitch_pdb_misc('2016', '2.10');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+       { name => 'sample_point', type => 'sample_point', no_validate => 1,
+         desc => 'The ID of the current sample point (0 if first invocation)' }
+    );
+
+    @outargs = (
+       { name => 'next_sample_point', type => 'sample_point',
+         desc => "The next sample point's ID" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpSamplePoint *sp = gimp_image_get_next_sample_point (image, sample_point,
+                                                          &success);
+
+  if (sp)
+    next_sample_point = gimp_sample_point_get_ID (sp);
+
+  if (! success)
+    g_set_error (error, GIMP_PDB_ERROR, GIMP_PDB_ERROR_INVALID_ARGUMENT,
+                 _("Image '%s' (%d) does not contain sample point with ID %d"),
+                 gimp_image_get_display_name (image),
+                 gimp_image_get_ID (image),
+                 sample_point);
+}
+CODE
+    );
+}
+
+sub image_get_sample_point_position {
+    $blurb = 'Get position of a sample point on an image.';
+
+    $help = <<'HELP';
+This procedure takes an image and a sample point ID as input and
+returns the position of the sample point relative to the top and left
+of the image.
+HELP
+
+    &mitch_pdb_misc('2016', '2.10');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+       { name => 'sample_point', type => 'sample_point',
+         desc => 'The guide' }
+    );
+
+    @outargs = (
+       { name => 'position_x', type => 'int32',
+          libdef => 'G_MININT',
+         desc => "The sample points's position relative to top of image" },
+       { name => 'position_y', type => 'int32',
+          libdef => 'G_MININT',
+         desc => "The sample points's position relative to top of image" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpSamplePoint *sp = gimp_pdb_image_get_sample_point (image, sample_point,
+                                                         error);
+
+  if (sp)
+    gimp_sample_point_get_position (sp, &position_x, &position_y);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+@headers = qw("core/gimpsamplepoint.h"
+              "core/gimpimage-sample-points.h"
+              "gimppdb-utils.h"
+              "gimppdberror.h"
+              "gimp-intl.h");
+
+@procs = qw(image_add_sample_point
+            image_delete_sample_point
+            image_find_next_sample_point
+           image_get_sample_point_position);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Image Sample Point procedures';
+$doc_title = 'gimpimagesamplepoints';
+$doc_short_desc = 'Functions for manipulating an image\'s sample points.';
+$doc_long_desc = 'Functions for manipulating an image\'s sample points.';
+
+1;
diff --git a/pdb/groups/image_select.pdb b/pdb/groups/image_select.pdb
new file mode 100644
index 0000000..1db4820
--- /dev/null
+++ b/pdb/groups/image_select.pdb
@@ -0,0 +1,438 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub image_select_color {
+    $blurb = <<'BLURB';
+Create a selection by selecting all pixels (in the specified drawable)
+with the same (or similar) color to that specified.
+BLURB
+
+    $help = <<'HELP';
+This tool creates a selection over the specified image. A by-color
+selection is determined by the supplied color under the constraints of
+the current context settings. Essentially, all pixels (in the drawable)
+that have color sufficiently close to the specified color (as
+determined by the threshold and criterion context values) are included
+in the selection. To select transparent regions, the color specified
+must also have minimum alpha.
+
+This procedure is affected by the following context setters:
+gimp_context_set_antialias(), gimp_context_set_feather(),
+gimp_context_set_feather_radius(), gimp_context_set_sample_merged(),
+gimp_context_set_sample_criterion(), gimp_context_set_sample_threshold(),
+gimp_context_set_sample_transparent().
+
+In the case of a merged sampling, the supplied drawable is ignored.
+HELP
+
+    &david_pdb_misc('2010', '2.8');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The affected image' },
+       { name => 'operation', type => 'enum GimpChannelOps',
+         desc => 'The selection operation' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+       { name => 'color', type => 'color',
+         desc => 'The color to select' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPDBContext *pdb_context = GIMP_PDB_CONTEXT (context);
+
+  if (pdb_context->sample_merged ||
+      gimp_pdb_item_is_attached (GIMP_ITEM (drawable), image, 0, error))
+    {
+      gimp_channel_select_by_color (gimp_image_get_mask (image), drawable,
+                                    pdb_context->sample_merged,
+                                    &color,
+                                    pdb_context->sample_threshold,
+                                    pdb_context->sample_transparent,
+                                    pdb_context->sample_criterion,
+                                    operation,
+                                    pdb_context->antialias,
+                                    pdb_context->feather,
+                                    pdb_context->feather_radius_x,
+                                    pdb_context->feather_radius_y);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+sub image_select_contiguous_color {
+    $blurb = <<'BLURB';
+Create a selection by selecting all pixels around specified coordinates
+with the same (or similar) color to that at the coordinates.
+BLURB
+
+    $help = <<'HELP';
+This tool creates a contiguous selection over the specified image. A
+contiguous color selection is determined by a seed fill under the
+constraints of the current context settings.  Essentially, the color
+at the specified coordinates (in the drawable) is measured and the
+selection expands outwards from that point to any adjacent pixels
+which are not significantly different (as determined by the threshold
+and criterion context settings). This process continues until no more
+expansion is possible. If antialiasing is turned on, the final
+selection mask will contain intermediate values based on close misses
+to the threshold bar at pixels along the seed fill boundary.
+
+This procedure is affected by the following context setters:
+gimp_context_set_antialias(), gimp_context_set_feather(),
+gimp_context_set_feather_radius(), gimp_context_set_sample_merged(),
+gimp_context_set_sample_criterion(), gimp_context_set_sample_threshold(),
+gimp_context_set_sample_transparent(), gimp_context_set_diagonal_neighbors().
+
+In the case of a merged sampling, the supplied drawable is ignored.
+If the sample is merged, the specified coordinates are relative to the
+image origin; otherwise, they are relative to the drawable's origin.
+HELP
+
+    &david_pdb_misc('2010', '2.8');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The affected image' },
+       { name => 'operation', type => 'enum GimpChannelOps',
+         desc => 'The selection operation' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+       { name => 'x', type => 'float',
+         desc => 'x coordinate of initial seed fill point: (image
+                  coordinates)' },
+       { name => 'y', type => 'float',
+         desc => 'y coordinate of initial seed fill point: (image
+                  coordinates)' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPDBContext *pdb_context = GIMP_PDB_CONTEXT (context);
+
+  if (pdb_context->sample_merged ||
+      gimp_pdb_item_is_attached (GIMP_ITEM (drawable), image, 0, error))
+    {
+
+      gimp_channel_select_fuzzy (gimp_image_get_mask (image),
+                                 drawable,
+                                 pdb_context->sample_merged,
+                                 x, y,
+                                 pdb_context->sample_threshold,
+                                 pdb_context->sample_transparent,
+                                 pdb_context->sample_criterion,
+                                 pdb_context->diagonal_neighbors,
+                                 operation,
+                                 pdb_context->antialias,
+                                 pdb_context->feather,
+                                 pdb_context->feather_radius_x,
+                                 pdb_context->feather_radius_y);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+sub image_select_rectangle {
+    $blurb = 'Create a rectangular selection over the specified image;';
+
+    $help = <<'HELP';
+This tool creates a rectangular selection over the specified
+image. The rectangular region can be either added to, subtracted from,
+or replace the contents of the previous selection mask.
+
+This procedure is affected by the following context setters:
+gimp_context_set_feather(), gimp_context_set_feather_radius().
+HELP
+
+    &mitch_pdb_misc('2010', '2.8');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+       { name => 'operation', type => 'enum GimpChannelOps',
+         desc => 'The selection operation' },
+       { name => 'x', type => 'float',
+         desc => 'x coordinate of upper-left corner of rectangle' },
+       { name => 'y', type => 'float',
+         desc => 'y coordinate of upper-left corner of rectangle' },
+       { name => 'width', type => '0 < float',
+         desc => 'The width of the rectangle' },
+       { name => 'height', type => '0 < float',
+         desc => 'The height of the rectangle' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPDBContext *pdb_context = GIMP_PDB_CONTEXT (context);
+
+  gimp_channel_select_rectangle (gimp_image_get_mask (image),
+                                 (gint) x, (gint) y,
+                                 (gint) width, (gint) height,
+                                 operation,
+                                 pdb_context->feather,
+                                 pdb_context->feather_radius_x,
+                                 pdb_context->feather_radius_y,
+                                 TRUE);
+}
+CODE
+    );
+}
+
+
+sub image_select_round_rectangle {
+    $blurb = 'Create a rectangular selection with round corners over the specified image;';
+
+    $help = <<'HELP';
+This tool creates a rectangular selection with round corners over the
+specified image. The rectangular region can be either added to,
+subtracted from, or replace the contents of the previous selection
+mask.
+
+This procedure is affected by the following context setters:
+gimp_context_set_antialias(), gimp_context_set_feather(),
+gimp_context_set_feather_radius().
+HELP
+
+    &martin_pdb_misc('2010', '2.8');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+       { name => 'operation', type => 'enum GimpChannelOps',
+         desc => 'The selection operation' },
+       { name => 'x', type => 'float',
+         desc => 'x coordinate of upper-left corner of rectangle' },
+       { name => 'y', type => 'float',
+         desc => 'y coordinate of upper-left corner of rectangle' },
+       { name => 'width', type => '0 < float',
+         desc => 'The width of the rectangle' },
+       { name => 'height', type => '0 < float',
+         desc => 'The height of the rectangle' },
+        { name => 'corner_radius_x', type => '0 < float < GIMP_MAX_IMAGE_SIZE',
+          desc => 'The corner radius in X direction' },
+        { name => 'corner_radius_y', type => '0 < float < GIMP_MAX_IMAGE_SIZE',
+          desc => 'The corner radius in Y direction' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPDBContext *pdb_context = GIMP_PDB_CONTEXT (context);
+
+  gimp_channel_select_round_rect (gimp_image_get_mask (image),
+                                  (gint) x, (gint) y,
+                                  (gint) width, (gint) height,
+                                  corner_radius_x,
+                                  corner_radius_y,
+                                  operation,
+                                  pdb_context->antialias,
+                                  pdb_context->feather,
+                                  pdb_context->feather_radius_x,
+                                  pdb_context->feather_radius_y,
+                                  TRUE);
+}
+CODE
+    );
+}
+
+
+sub image_select_ellipse {
+    $blurb = 'Create an elliptical selection over the specified image.';
+
+    $help = <<'HELP';
+This tool creates an elliptical selection over the specified
+image. The elliptical region can be either added to, subtracted from,
+or replace the contents of the previous selection mask.
+
+This procedure is affected by the following context setters:
+gimp_context_set_antialias(), gimp_context_set_feather(),
+gimp_context_set_feather_radius().
+HELP
+
+    &mitch_pdb_misc('2010', '2.8');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+       { name => 'operation', type => 'enum GimpChannelOps',
+         desc => 'The selection operation' },
+       { name => 'x', type => 'float',
+         desc => 'x coordinate of upper-left corner of ellipse bounding box' },
+       { name => 'y', type => 'float',
+         desc => 'y coordinate of upper-left corner of ellipse bounding box' },
+       { name => 'width', type => '0 < float',
+         desc => 'The width of the ellipse' },
+       { name => 'height', type => '0 < float',
+         desc => 'The height of the ellipse' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPDBContext *pdb_context = GIMP_PDB_CONTEXT (context);
+
+  gimp_channel_select_ellipse (gimp_image_get_mask (image),
+                               (gint) x, (gint) y,
+                               (gint) width, (gint) height,
+                               operation,
+                               pdb_context->antialias,
+                               pdb_context->feather,
+                               pdb_context->feather_radius_x,
+                               pdb_context->feather_radius_y,
+                               TRUE);
+}
+CODE
+    );
+}
+
+
+sub image_select_polygon {
+    $blurb = 'Create a polygonal selection over the specified image.';
+
+    $help = <<'HELP';
+This tool creates a polygonal selection over the specified image. The
+polygonal region can be either added to, subtracted from, or replace
+the contents of the previous selection mask. The polygon is specified
+through an array of floating point numbers and its length. The length
+of array must be 2n, where n is the number of points. Each point is
+defined by 2 floating point values which correspond to the x and y
+coordinates. If the final point does not connect to the starting
+point, a connecting segment is automatically added.
+
+This procedure is affected by the following context setters:
+gimp_context_set_antialias(), gimp_context_set_feather(),
+gimp_context_set_feather_radius().
+HELP
+
+    &mitch_pdb_misc('2010', '2.8');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+       { name => 'operation', type => 'enum GimpChannelOps',
+         desc => 'The selection operation' },
+        { name => 'segs', type => 'floatarray',
+          desc => 'Array of points: { p1.x, p1.y, p2.x, p2.y, ...,
+                   pn.x, pn.y}',
+          array => { type => '2 <= int32',
+                     desc => 'Number of points (count 1 coordinate as two
+                              points)' } }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpPDBContext *pdb_context = GIMP_PDB_CONTEXT (context);
+
+  gimp_channel_select_polygon (gimp_image_get_mask (image),
+                               _("Free Select"),
+                               num_segs / 2,
+                               (GimpVector2 *) segs,
+                               operation,
+                               pdb_context->antialias,
+                               pdb_context->feather,
+                               pdb_context->feather_radius_x,
+                               pdb_context->feather_radius_y,
+                               TRUE);
+}
+CODE
+    );
+}
+
+
+sub image_select_item {
+    $blurb = 'Transforms the specified item into a selection';
+
+    $help = <<'HELP';
+This procedure renders the item's outline into the current selection
+of the image the item belongs to. What exactly the item's outline is
+depends on the item type: for layers, it's the layer's alpha channel,
+for vectors the vector's shape.
+
+This procedure is affected by the following context setters:
+gimp_context_set_antialias(), gimp_context_set_feather(),
+gimp_context_set_feather_radius().
+HELP
+
+    &mitch_pdb_misc('2010', '2.8');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+        { name => 'operation', type => 'enum GimpChannelOps',
+          desc => 'The desired operation with current selection' },
+        { name => 'item', type => 'item',
+          desc => 'The item to render to the selection' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (item, image, 0, error))
+    {
+      GimpPDBContext *pdb_context = GIMP_PDB_CONTEXT (context);
+
+      gimp_item_to_selection (item, operation,
+                              pdb_context->antialias,
+                              pdb_context->feather,
+                              pdb_context->feather_radius_x,
+                              pdb_context->feather_radius_y);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+
+}
+
+
+@headers = qw("libgimpbase/gimpbase.h"
+              "core/gimpchannel-select.h"
+              "gimppdb-utils.h"
+              "gimppdbcontext.h"
+              "gimp-intl.h");
+
+@procs = qw(image_select_color
+            image_select_contiguous_color
+            image_select_rectangle
+            image_select_round_rectangle
+            image_select_ellipse
+            image_select_polygon
+            image_select_item);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Selection procedures';
+$doc_title = 'gimpimageselect';
+$doc_short_desc = "Modify the image's selection.";
+$doc_long_desc = "Functions to modify the image's selection.";
+
+1;
diff --git a/pdb/groups/image_transform.pdb b/pdb/groups/image_transform.pdb
new file mode 100644
index 0000000..51c587b
--- /dev/null
+++ b/pdb/groups/image_transform.pdb
@@ -0,0 +1,276 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub image_resize {
+    $blurb = 'Resize the image to the specified extents.';
+
+    $help = <<'HELP';
+This procedure resizes the image so that it's new width and height are
+equal to the supplied parameters. Offsets are also provided which
+describe the position of the previous image's content. All channels
+within the image are resized according to the specified parameters;
+this includes the image selection mask. All layers within the image
+are repositioned according to the specified offsets.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'new_width', type => '1 <= int32 <= GIMP_MAX_IMAGE_SIZE',
+          desc => 'New image width' },
+        { name => 'new_height', type => '1 <= int32 <= GIMP_MAX_IMAGE_SIZE',
+          desc => 'New image height' },
+        { name => 'offx', type => 'int32',
+          desc => 'x offset between upper left corner of old and
+                   new images: (new - old)' },
+        { name => 'offy', type => 'int32',
+          desc => 'y offset between upper left corner of old and
+                   new images: (new - old)' }
+    );
+
+    %invoke = (
+        headers => [ qw("core/gimpimage-resize.h") ],
+        code => <<'CODE'
+{
+  gimp_image_resize (image, context,
+                     new_width, new_height, offx, offy, NULL);
+}
+CODE
+    );
+}
+
+sub image_resize_to_layers {
+    $blurb = 'Resize the image to fit all layers.';
+
+    $help = <<'HELP';
+This procedure resizes the image to the bounding box of all layers of
+the image. All channels within the image are resized to the new size;
+this includes the image selection mask. All layers within the image
+are repositioned to the new image area.
+HELP
+
+    &simon_pdb_misc('2004', '2.2');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' }
+    );
+
+    %invoke = (
+        headers => [ qw("core/gimpimage-resize.h") ],
+        code => <<'CODE'
+{
+  gimp_image_resize_to_layers (image, context, NULL);
+}
+CODE
+    );
+}
+
+sub image_scale {
+    $blurb = 'Scale the image using the default interpolation method.';
+
+    $help = <<'HELP';
+
+This procedure scales the image so that its new width and height are
+equal to the supplied parameters. All layers and channels within the
+image are scaled according to the specified parameters; this includes
+the image selection mask. The interpolation method used can be set
+with gimp_context_set_interpolation().
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'new_width', type => '1 <= int32 <= GIMP_MAX_IMAGE_SIZE',
+          desc => 'New image width' },
+        { name => 'new_height', type => '1 <= int32 <= GIMP_MAX_IMAGE_SIZE',
+          desc => 'New image height' }
+    );
+
+    %invoke = (
+        headers => [ qw("core/gimpimage-scale.h") ],
+        code => <<'CODE'
+{
+  GimpPDBContext *pdb_context = GIMP_PDB_CONTEXT (context);
+
+  if (progress)
+    gimp_progress_start (progress, FALSE, _("Scaling"));
+
+  gimp_image_scale (image, new_width, new_height,
+                    pdb_context->interpolation,
+                    progress);
+
+  if (progress)
+    gimp_progress_end (progress);
+}
+CODE
+    );
+}
+
+sub image_scale_full {
+    &std_pdb_deprecated('gimp-image-scale');
+    &neo_pdb_misc('2008', '2.6');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'new_width', type => '1 <= int32 <= GIMP_MAX_IMAGE_SIZE',
+          desc => 'New image width' },
+        { name => 'new_height', type => '1 <= int32 <= GIMP_MAX_IMAGE_SIZE',
+          desc => 'New image height' },
+        { name => 'interpolation', type => 'enum GimpInterpolationType',
+          desc => 'Type of interpolation' }
+    );
+
+    %invoke = (
+        headers => [ qw("core/gimpimage-scale.h") ],
+        code => <<'CODE'
+{
+  if (progress)
+    gimp_progress_start (progress, FALSE, _("Scaling"));
+
+  gimp_image_scale (image, new_width, new_height, interpolation, progress);
+
+  if (progress)
+    gimp_progress_end (progress);
+}
+CODE
+    );
+}
+
+sub image_crop {
+    $blurb = 'Crop the image to the specified extents.';
+
+    $help = <<'HELP';
+This procedure crops the image so that it's new width and height are
+equal to the supplied parameters. Offsets are also provided which
+describe the position of the previous image's content. All channels
+and layers within the image are cropped to the new image extents; this
+includes the image selection mask. If any parameters are out of range,
+an error is returned.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'new_width', type => '1 <= int32 <= GIMP_MAX_IMAGE_SIZE',
+          desc => 'New image width: (0 < new_width <= width)' },
+        { name => 'new_height', type => '1 <= int32 <= GIMP_MAX_IMAGE_SIZE',
+          desc => 'New image height: (0 < new_height <= height)' },
+        { name => 'offx', type => '0 <= int32',
+          desc => 'X offset: (0 <= offx <= (width - new_width))' },
+        { name => 'offy', type => '0 <= int32',
+          desc => 'Y offset: (0 <= offy <= (height - new_height))' }
+    );
+
+    %invoke = (
+        headers => [ qw("core/gimpimage-crop.h") ],
+        code => <<'CODE'
+{
+  if (new_width  >  gimp_image_get_width  (image)              ||
+      new_height >  gimp_image_get_height (image)              ||
+      offx       > (gimp_image_get_width  (image) - new_width) ||
+      offy       > (gimp_image_get_height (image) - new_height))
+    success = FALSE;
+  else
+    gimp_image_crop (image, context, GIMP_FILL_TRANSPARENT,
+                     offx, offy, new_width, new_height,
+                     TRUE);
+}
+CODE
+    );
+}
+
+sub image_flip {
+    $blurb = 'Flips the image horizontally or vertically.';
+
+    $help = <<'HELP';
+This procedure flips (mirrors) the image.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'flip_type',
+          type => 'enum GimpOrientationType (no GIMP_ORIENTATION_UNKNOWN)',
+          desc => 'Type of flip' }
+    );
+
+    %invoke = (
+        headers => [ qw("core/gimpimage-flip.h") ],
+        code => <<'CODE'
+{
+  gimp_image_flip (image, context, flip_type, NULL);
+}
+CODE
+    );
+}
+
+sub image_rotate {
+    $blurb = 'Rotates the image by the specified degrees.';
+    $help = 'This procedure rotates the image.';
+
+    &mitch_pdb_misc('2003');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'rotate_type', type => 'enum GimpRotationType',
+          desc => 'Angle of rotation' }
+    );
+
+    %invoke = (
+        headers => [ qw("core/gimpimage-rotate.h") ],
+        code => <<'CODE'
+{
+  if (progress)
+    gimp_progress_start (progress, FALSE, _("Rotating"));
+
+  gimp_image_rotate (image, context, rotate_type, progress);
+
+  if (progress)
+    gimp_progress_end (progress);
+}
+CODE
+    );
+}
+
+@headers = qw("core/gimpprogress.h"
+              "gimppdbcontext.h"
+              "gimp-intl.h");
+
+@procs = qw(image_resize image_resize_to_layers
+            image_scale image_scale_full
+            image_crop image_flip image_rotate);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Image Transform';
+$doc_title = 'gimpimagetransform';
+$doc_short_desc = 'Transformations on images.';
+$doc_long_desc = 'Operations to scale, resize, crop, flip and rotate images.';
+
+1;
diff --git a/pdb/groups/image_undo.pdb b/pdb/groups/image_undo.pdb
new file mode 100644
index 0000000..3c81083
--- /dev/null
+++ b/pdb/groups/image_undo.pdb
@@ -0,0 +1,306 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub image_undo_group_start {
+    $blurb = 'Starts a group undo.';
+
+    $help = <<'HELP';
+This function is used to start a group undo--necessary for logically combining
+two or more undo operations into a single operation. This call must be used in
+conjunction with a gimp_image_undo_group_end() call.
+HELP
+
+    &std_pdb_misc;
+    $date = '1997';
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The ID of the image in which to open an undo group' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPlugIn  *plug_in   = gimp->plug_in_manager->current_plug_in;
+  const gchar *undo_desc = NULL;
+
+  if (plug_in)
+    {
+      success = gimp_plug_in_cleanup_undo_group_start (plug_in, image);
+
+      if (success)
+        undo_desc = gimp_plug_in_get_undo_desc (plug_in);
+    }
+
+  if (success)
+    gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_MISC, undo_desc);
+}
+CODE
+    );
+}
+
+sub image_undo_group_end {
+    $blurb = 'Finish a group undo.';
+
+    $help = <<'HELP';
+This function must be called once for each gimp_image_undo_group_start() call
+that is made.
+HELP
+
+    &std_pdb_misc;
+    $date = '1997';
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The ID of the image in which to close an undo group' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPlugIn *plug_in = gimp->plug_in_manager->current_plug_in;
+
+  if (plug_in)
+    success = gimp_plug_in_cleanup_undo_group_end (plug_in, image);
+
+  if (success)
+    gimp_image_undo_group_end (image);
+}
+CODE
+    );
+}
+
+sub image_undo_is_enabled {
+    $blurb = "Check if the image's undo stack is enabled.";
+
+    $help = <<'HELP';
+This procedure checks if the image's undo stack is currently enabled or
+disabled.  This is useful when several plug-ins or scripts call each other
+and want to check if their caller has already used gimp_image_undo_disable()
+or gimp_image_undo_freeze().
+HELP
+
+    &raphael_pdb_misc('1999');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc  => 'The image' }
+    );
+
+    @outargs = (
+       { name => 'enabled', type => 'boolean',
+         desc => 'TRUE if undo is enabled for this image' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  enabled = gimp_image_undo_is_enabled (image);
+}
+CODE
+    );
+}
+
+sub image_undo_disable {
+    $blurb = "Disable the image's undo stack.";
+
+    $help = <<'HELP';
+This procedure disables the image's undo stack, allowing subsequent operations
+to ignore their undo steps. This is generally called in conjunction with
+gimp_image_undo_enable() to temporarily disable an image undo stack. This is
+advantageous because saving undo steps can be time and memory intensive.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' }
+    );
+
+    @outargs = (
+       { name => 'disabled', type => 'boolean',
+         desc => 'TRUE if the image undo has been disabled' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+#if 0
+  GimpPlugIn *plug_in = gimp->plug_in_manager->current_plug_in;
+
+  if (plug_in)
+    success = gimp_plug_in_cleanup_undo_disable (plug_in, image);
+#endif
+
+  if (success)
+    disabled = gimp_image_undo_disable (image);
+}
+CODE
+    );
+}
+
+sub image_undo_enable {
+    $blurb = "Enable the image's undo stack.";
+
+    $help = <<'HELP';
+This procedure enables the image's undo stack, allowing subsequent operations
+to store their undo steps. This is generally called in conjunction with
+gimp_image_undo_disable() to temporarily disable an image undo stack.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' }
+    );
+
+    @outargs = (
+       { name => 'enabled', type => 'boolean',
+         desc => 'TRUE if the image undo has been enabled' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+#if 0
+  GimpPlugIn *plug_in = gimp->plug_in_manager->current_plug_in;
+
+  if (plug_in)
+    success = gimp_plug_in_cleanup_undo_enable (plug_in, image);
+#endif
+
+  if (success)
+    enabled = gimp_image_undo_enable (image);
+}
+CODE
+    );
+}
+
+sub image_undo_freeze {
+    $blurb = "Freeze the image's undo stack.";
+
+    &adam_pdb_misc('1999');
+
+    $help = <<'HELP';
+This procedure freezes the image's undo stack, allowing subsequent
+operations to ignore their undo steps.  This is generally called in
+conjunction with gimp_image_undo_thaw() to temporarily disable an
+image undo stack.  This is advantageous because saving undo steps can
+be time and memory intensive.  gimp_image_undo_freeze() /
+gimp_image_undo_thaw() and gimp_image_undo_disable() /
+gimp_image_undo_enable() differ in that the former does not free up
+all undo steps when undo is thawed, so is more suited to interactive
+in-situ previews.  It is important in this case that the image is back
+to the same state it was frozen in before thawing, else 'undo'
+behaviour is undefined.
+HELP
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' }
+    );
+
+    @outargs = (
+       { name => 'frozen', type => 'boolean',
+         desc => 'TRUE if the image undo has been frozen' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+#if 0
+  GimpPlugIn *plug_in = gimp->plug_in_manager->current_plug_in;
+
+  if (plug_in)
+    success = gimp_plug_in_cleanup_undo_freeze (plug_in, image);
+#endif
+
+  if (success)
+    frozen = gimp_image_undo_freeze (image);
+}
+CODE
+    );
+}
+
+sub image_undo_thaw {
+    $blurb = "Thaw the image's undo stack.";
+
+    &adam_pdb_misc('1999');
+
+    $help = <<'HELP';
+This procedure thaws the image's undo stack, allowing subsequent
+operations to store their undo steps. This is generally called in
+conjunction with gimp_image_undo_freeze() to temporarily freeze an
+image undo stack.  gimp_image_undo_thaw() does NOT free the undo stack
+as gimp_image_undo_enable() does, so is suited for situations where
+one wishes to leave the undo stack in the same state in which one
+found it despite non-destructively playing with the image in the
+meantime.  An example would be in-situ plug-in previews.  Balancing
+freezes and thaws and ensuring image consistency is the responsibility
+of the caller.
+HELP
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' }
+    );
+
+    @outargs = (
+       { name => 'thawed', type => 'boolean',
+         desc => 'TRUE if the image undo has been thawed' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+#if 0
+  GimpPlugIn *plug_in = gimp->plug_in_manager->current_plug_in;
+
+  if (plug_in)
+    success = gimp_plug_in_cleanup_undo_thaw (plug_in, image);
+#endif
+
+  if (success)
+    thawed = gimp_image_undo_thaw (image);
+}
+CODE
+    );
+}
+
+
+@headers = qw("core/gimp.h"
+              "core/gimpimage-undo.h"
+              "plug-in/gimpplugin.h"
+              "plug-in/gimpplugin-cleanup.h"
+              "plug-in/gimppluginmanager.h");
+
+@procs = qw(image_undo_group_start image_undo_group_end
+            image_undo_is_enabled
+            image_undo_disable image_undo_enable
+            image_undo_freeze image_undo_thaw);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Image Undo';
+$doc_title = 'gimpimageundo';
+$doc_short_desc = 'Control of image undo/redo.';
+$doc_long_desc = 'Control of image undo/redo.';
+
+1;
diff --git a/pdb/groups/item.pdb b/pdb/groups/item.pdb
new file mode 100644
index 0000000..d01fc8c
--- /dev/null
+++ b/pdb/groups/item.pdb
@@ -0,0 +1,992 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub item_is_valid {
+    $blurb = 'Returns TRUE if the item is valid.';
+
+    $help = <<'HELP';
+This procedure checks if the given item ID is valid and refers to an
+existing item.
+HELP
+
+    &neo_pdb_misc('2007', '2.8');
+
+    @inargs = (
+       { name => 'item', type => 'item', no_validate => 1,
+         desc => 'The item to check' }
+    );
+
+    @outargs = (
+       { name => 'valid', type => 'boolean',
+         desc => 'Whether the item ID is valid' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  valid = (GIMP_IS_ITEM (item) &&
+           ! gimp_item_is_removed (GIMP_ITEM (item)));
+}
+CODE
+    );
+}
+
+sub item_get_image {
+    $blurb = "Returns the item's image.";
+
+    $help = "This procedure returns the item's image.";
+
+    &std_pdb_misc;
+    $since = '2.8';
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The item' }
+    );
+
+    @outargs = (
+       { name => 'image', type => 'image',
+         desc => "The item's image" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  image = gimp_item_get_image (GIMP_ITEM (item));
+}
+CODE
+    );
+}
+
+sub item_delete {
+    $blurb = 'Delete a item.';
+
+    $help = <<'HELP';
+This procedure deletes the specified item. This must not be done if the
+image containing this item was already deleted or if the item was
+already removed from the image. The only case in which this procedure is
+useful is if you want to get rid of a item which has not yet been
+added to an image.
+HELP
+
+    &std_pdb_misc;
+    $since = '2.8';
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The item to delete' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (g_object_is_floating (item))
+    {
+      g_object_ref_sink (item);
+      g_object_unref (item);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub item_is_drawable {
+    $blurb = 'Returns whether the item is a drawable.';
+
+    $help = <<HELP;
+This procedure returns TRUE if the specified item is a drawable.
+HELP
+
+    &std_pdb_misc;
+    $since = '2.8';
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The item' }
+    );
+
+    @outargs = (
+       { name => 'drawable', type => 'boolean',
+         desc => 'TRUE if the item is a drawable, FALSE otherwise' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  drawable = GIMP_IS_DRAWABLE (item);
+}
+CODE
+    );
+}
+
+sub item_is_layer {
+    $blurb = 'Returns whether the item is a layer.';
+
+    $help = <<HELP;
+This procedure returns TRUE if the specified item is a layer.
+HELP
+
+    &std_pdb_misc;
+    $since = '2.8';
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The item' }
+    );
+
+    @outargs = (
+       { name => 'layer', type => 'boolean',
+         desc => 'TRUE if the item is a layer, FALSE otherwise' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  layer = GIMP_IS_LAYER (item);
+}
+CODE
+    );
+}
+
+sub item_is_text_layer {
+    $blurb = 'Returns whether the item is a text layer.';
+
+    $help = <<'HELP';
+This procedure returns TRUE if the specified item is a text layer.
+HELP
+
+    &mitch_pdb_misc('2010', '2.8');
+
+    @inargs = (
+        { name => 'item', type => 'item',
+         desc => 'The item' }
+    );
+
+    @outargs = (
+        { name => 'text_layer', type => 'boolean',
+         desc => 'TRUE if the item is a text layer, FALSE otherwise.' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  text_layer = gimp_item_is_text_layer (item);
+}
+CODE
+    );
+}
+
+sub item_is_channel {
+    $blurb = 'Returns whether the item is a channel.';
+
+    $help = <<HELP;
+This procedure returns TRUE if the specified item is a channel.
+HELP
+
+    &std_pdb_misc;
+    $since = '2.8';
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The item' }
+    );
+
+    @outargs = (
+       { name => 'channel', type => 'boolean',
+         desc => 'TRUE if the item is a channel, FALSE otherwise' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  channel = GIMP_IS_CHANNEL (item);
+}
+CODE
+    );
+}
+
+sub item_is_layer_mask {
+    $blurb = 'Returns whether the item is a layer mask.';
+
+    $help = <<HELP;
+This procedure returns TRUE if the specified item is a layer mask.
+HELP
+
+    &std_pdb_misc;
+    $since = '2.8';
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The item' }
+    );
+
+    @outargs = (
+       { name => 'layer_mask', type => 'boolean',
+         desc => 'TRUE if the item is a layer mask, FALSE otherwise' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  layer_mask = GIMP_IS_LAYER_MASK (item);
+}
+CODE
+    );
+}
+
+sub item_is_selection {
+    $blurb = 'Returns whether the item is a selection.';
+
+    $help = <<HELP;
+This procedure returns TRUE if the specified item is a selection.
+HELP
+
+    &std_pdb_misc;
+    $since = '2.8';
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The item' }
+    );
+
+    @outargs = (
+       { name => 'selection', type => 'boolean',
+         desc => 'TRUE if the item is a selection, FALSE otherwise' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  selection = GIMP_IS_SELECTION (item);
+}
+CODE
+    );
+}
+
+sub item_is_vectors {
+    $blurb = 'Returns whether the item is a vectors.';
+
+    $help = <<HELP;
+This procedure returns TRUE if the specified item is a vectors.
+HELP
+
+    &std_pdb_misc;
+    $since = '2.8';
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The item' }
+    );
+
+    @outargs = (
+       { name => 'vectors', type => 'boolean',
+         desc => 'TRUE if the item is a vectors, FALSE otherwise' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  vectors = GIMP_IS_VECTORS (item);
+}
+CODE
+    );
+}
+
+sub item_is_group {
+    $blurb = 'Returns whether the item is a group item.';
+
+    $help = <<HELP;
+This procedure returns TRUE if the specified item is a group item which
+can have children.
+HELP
+
+    &mitch_pdb_misc('2010', '2.8');
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The item' }
+    );
+
+    @outargs = (
+       { name => 'group', type => 'boolean',
+         desc => 'TRUE if the item is a group, FALSE otherwise' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  group = (gimp_viewable_get_children (GIMP_VIEWABLE (item)) != NULL);
+}
+CODE
+    );
+}
+
+sub item_get_parent {
+    $blurb = "Returns the item's parent item.";
+
+    $help = <<HELP;
+This procedure returns the item's parent item, if any.
+HELP
+
+    &mitch_pdb_misc('2010', '2.8');
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The item' }
+    );
+
+    @outargs = (
+       { name => 'parent', type => 'item',
+         desc => "The item's parent item" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  parent = gimp_item_get_parent (item);
+}
+CODE
+    );
+}
+
+sub item_get_children {
+    $blurb = "Returns the item's list of children.";
+
+    $help = <<HELP;
+This procedure returns the list of items which are children of the specified
+item. The order is topmost to bottommost.
+HELP
+
+    &mitch_pdb_misc('2010', '2.8');
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The item' }
+    );
+
+    @outargs = (
+       { name => 'child_ids', type => 'int32array',
+         desc => "The item's list of children",
+         array => { name => 'num_children',
+                    desc => "The item's number of children" } }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpContainer *children = gimp_viewable_get_children (GIMP_VIEWABLE (item));
+
+  if (children)
+    {
+      num_children = gimp_container_get_n_children (children);
+
+      if (num_children)
+        {
+          GList *list;
+          gint   i;
+
+          child_ids = g_new (gint32, num_children);
+
+          for (list = GIMP_LIST (children)->queue->head, i = 0;
+               list;
+               list = g_list_next (list), i++)
+            {
+              child_ids[i] = gimp_item_get_ID (GIMP_ITEM (list->data));
+            }
+        }
+    }
+  else
+    success = FALSE;
+
+}
+CODE
+    );
+}
+
+sub item_get_expanded {
+    $blurb = 'Returns whether the item is expanded.';
+
+    $help = <<HELP;
+This procedure returns TRUE if the specified item is expanded.
+HELP
+
+    &ell_pdb_misc('2017', '2.10');
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The item' }
+    );
+
+    @outargs = (
+       { name => 'expanded', type => 'boolean',
+         desc => 'TRUE if the item is expanded, FALSE otherwise' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  expanded = gimp_viewable_get_expanded (GIMP_VIEWABLE (item));
+}
+CODE
+    );
+}
+
+sub item_set_expanded {
+    $blurb = 'Sets the expanded state of the item.';
+
+    $help = <<HELP;
+This procedure expands or collapses the item.
+HELP
+
+    &ell_pdb_misc('2017', '2.10');
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The item' },
+    { name => 'expanded', type => 'boolean',
+         desc => 'TRUE to expand the item, FALSE to collapse the item' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_viewable_set_expanded (GIMP_VIEWABLE (item), expanded);
+}
+CODE
+    );
+}
+
+sub item_get_name {
+    $blurb = "Get the name of the specified item.";
+
+    $help = "This procedure returns the specified item's name.";
+
+    &std_pdb_misc;
+    $since = '2.8';
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The item' }
+    );
+
+    @outargs = (
+       { name => 'name', type => 'string',
+         desc => "The item name" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  name = g_strdup (gimp_object_get_name (item));
+}
+CODE
+    );
+}
+
+sub item_set_name {
+    $blurb = "Set the name of the specified item.";
+
+    $help = "This procedure sets the specified item's name.";
+
+    &std_pdb_misc;
+    $since = '2.8';
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The item' },
+       { name => 'name', type => 'string',
+         desc => "The new item name" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  success = gimp_item_rename (GIMP_ITEM (item), name, error);
+}
+CODE
+    );
+}
+
+sub item_get_visible {
+    $blurb = "Get the visibility of the specified item.";
+
+    $help = "This procedure returns the specified item's visibility.";
+
+    &std_pdb_misc;
+    $since = '2.8';
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The item' }
+    );
+
+    @outargs = (
+       { name => 'visible', type => 'boolean',
+         desc => "The item visibility" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  visible = gimp_item_get_visible (GIMP_ITEM (item));
+}
+CODE
+    );
+}
+
+sub item_set_visible {
+    $blurb = "Set the visibility of the specified item.";
+
+    $help = "This procedure sets the specified item's visibility.";
+
+    &std_pdb_misc;
+    $since = '2.8';
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The item' },
+       { name => 'visible', type => 'boolean',
+         desc => "The new item visibility" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_item_set_visible (GIMP_ITEM (item), visible, TRUE);
+}
+CODE
+    );
+}
+
+sub item_get_linked {
+    $blurb = "Get the linked state of the specified item.";
+
+    $help = "This procedure returns the specified item's linked state.";
+
+    &wolfgang_pdb_misc('1998', '2.8');
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The item' }
+    );
+
+    @outargs = (
+       { name => 'linked', type => 'boolean',
+         desc => "The item linked state (for moves)" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  linked = gimp_item_get_linked (GIMP_ITEM (item));
+}
+CODE
+    );
+}
+
+sub item_set_linked {
+    $blurb = "Set the linked state of the specified item.";
+
+    $help = "This procedure sets the specified item's linked state.";
+
+    &wolfgang_pdb_misc('1998', '2.8');
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The item' },
+       { name => 'linked', type => 'boolean',
+         desc => "The new item linked state" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_item_set_linked (GIMP_ITEM (item), linked, TRUE);
+}
+CODE
+    );
+}
+
+sub item_get_lock_content {
+    $blurb = "Get the 'lock content' state of the specified item.";
+
+    $help = "This procedure returns the specified item's lock content state.";
+
+    &mitch_pdb_misc('2009', '2.8');
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The item' }
+    );
+
+    @outargs = (
+       { name => 'lock_content', type => 'boolean',
+         desc => "Whether the item's contents are locked" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  lock_content = gimp_item_get_lock_content (GIMP_ITEM (item));
+}
+CODE
+    );
+}
+
+sub item_set_lock_content {
+    $blurb = "Set the 'lock content' state of the specified item.";
+
+    $help = "This procedure sets the specified item's lock content state.";
+
+    &mitch_pdb_misc('2009', '2.8');
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The item' },
+       { name => 'lock_content', type => 'boolean',
+         desc => "The new item 'lock content' state" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_item_can_lock_content (GIMP_ITEM (item)))
+    gimp_item_set_lock_content (GIMP_ITEM (item), lock_content, TRUE);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub item_get_lock_position {
+    $blurb = "Get the 'lock position' state of the specified item.";
+
+    $help = "This procedure returns the specified item's lock position state.";
+
+    &mitch_pdb_misc('2012', '2.10');
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The item' }
+    );
+
+    @outargs = (
+       { name => 'lock_position', type => 'boolean',
+         desc => "Whether the item's position is locked" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  lock_position = gimp_item_get_lock_position (GIMP_ITEM (item));
+}
+CODE
+    );
+}
+
+sub item_set_lock_position {
+    $blurb = "Set the 'lock position' state of the specified item.";
+
+    $help = "This procedure sets the specified item's lock position state.";
+
+    &mitch_pdb_misc('2009', '2.10');
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The item' },
+       { name => 'lock_position', type => 'boolean',
+         desc => "The new item 'lock position' state" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_item_can_lock_position (GIMP_ITEM (item)))
+    gimp_item_set_lock_position (GIMP_ITEM (item), lock_position, TRUE);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub item_get_color_tag {
+    $blurb = "Get the color tag of the specified item.";
+
+    $help = "This procedure returns the specified item's color tag.";
+
+    &mitch_pdb_misc('2016', '2.10');
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The item' }
+    );
+
+    @outargs = (
+       { name => 'color_tag', type => 'enum GimpColorTag',
+         desc => "The item's color tag" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  color_tag = gimp_item_get_color_tag (GIMP_ITEM (item));
+}
+CODE
+    );
+}
+
+sub item_set_color_tag {
+    $blurb = "Set the color tag of the specified item.";
+
+    $help = "This procedure sets the specified item's color tag.";
+
+    &mitch_pdb_misc('2016', '2.10');
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The item' },
+       { name => 'color_tag', type => 'enum GimpColorTag',
+         desc => "The new item color tag" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_item_set_color_tag (GIMP_ITEM (item), color_tag, TRUE);
+}
+CODE
+    );
+}
+
+sub item_get_tattoo {
+    $blurb = "Get the tattoo of the specified item.";
+
+    $help = <<'HELP';
+This procedure returns the specified item's tattoo. A tattoo is a
+unique and permanent identifier attached to a item that can be
+used to uniquely identify a item within an image even between
+sessions.
+HELP
+
+    &jay_pdb_misc('1998', '2.8');
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The item' }
+    );
+
+    @outargs = (
+       { name => 'tattoo', type => 'tattoo',
+         desc => "The item tattoo" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  tattoo = gimp_item_get_tattoo (GIMP_ITEM (item));
+}
+CODE
+    );
+}
+
+sub item_set_tattoo {
+    $blurb = "Set the tattoo of the specified item.";
+
+    $help = <<'HELP';
+This procedure sets the specified item's tattoo. A tattoo is a
+unique and permanent identifier attached to a item that can be
+used to uniquely identify a item within an image even between
+sessions.
+HELP
+
+    &jay_pdb_misc('1998', '2.8');
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The item' },
+       { name => 'tattoo', type => 'tattoo',
+         desc => "The new item tattoo" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_item_set_tattoo (GIMP_ITEM (item), tattoo);
+}
+CODE
+    );
+}
+
+sub item_attach_parasite {
+    $blurb = 'Add a parasite to an item.';
+
+    $help = <<'HELP';
+This procedure attaches a parasite to an item. It has no return values.
+HELP
+
+    &jay_pdb_misc('1998', '2.8');
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The item' },
+       { name => 'parasite', type => 'parasite',
+         desc => 'The parasite to attach to the item' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_item_parasite_validate (item, parasite, error))
+    gimp_item_parasite_attach (item, parasite, TRUE);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub item_detach_parasite {
+    $blurb = 'Removes a parasite from an item.';
+
+    $help = <<'HELP';
+This procedure detaches a parasite from an item. It has no return values.
+HELP
+
+    &jay_pdb_misc('1998', '2.8');
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The item' },
+       { name => 'name', type => 'string',
+         desc => 'The name of the parasite to detach from the item.' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_item_parasite_detach (item, name, TRUE);
+}
+CODE
+    );
+}
+
+sub item_get_parasite {
+    $blurb = 'Look up a parasite in an item';
+
+    $help = <<'HELP';
+Finds and returns the parasite that is attached to an item.
+HELP
+
+    &jay_pdb_misc('1998', '2.8');
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The item' },
+       { name => 'name', type => 'string',
+         desc => 'The name of the parasite to find' }
+    );
+
+    @outargs = (
+       { name => 'parasite', type => 'parasite',
+         desc => 'The found parasite' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  parasite = gimp_parasite_copy (gimp_item_parasite_find (item, name));
+
+  if (! parasite)
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub item_get_parasite_list {
+    $blurb = 'List all parasites.';
+    $help  = 'Returns a list of all parasites currently attached the an item.';
+
+    &marc_pdb_misc('1999', '2.8');
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The item' }
+    );
+
+    @outargs = (
+       { name => 'parasites', type => 'stringarray',
+         desc => 'The names of currently attached parasites',
+         array => { desc => 'The number of attached parasites' } }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  parasites = gimp_item_parasite_list (item, &num_parasites);
+}
+CODE
+    );
+}
+
+@headers = qw("core/gimplayermask.h"
+              "core/gimplist.h"
+              "core/gimpselection.h"
+              "text/gimptextlayer.h"
+             "vectors/gimpvectors.h"
+              "gimppdb-utils.h"
+              "gimppdbcontext.h"
+              "gimp-intl.h");
+
+@procs = qw(item_is_valid
+            item_get_image
+            item_delete
+            item_is_drawable
+            item_is_layer
+            item_is_text_layer
+           item_is_channel
+           item_is_layer_mask
+           item_is_selection
+           item_is_vectors
+           item_is_group
+            item_get_parent
+           item_get_children
+            item_get_expanded item_set_expanded
+            item_get_name item_set_name
+            item_get_visible item_set_visible
+            item_get_linked item_set_linked
+            item_get_lock_content item_set_lock_content
+            item_get_lock_position item_set_lock_position
+            item_get_color_tag item_set_color_tag
+            item_get_tattoo item_set_tattoo
+           item_attach_parasite item_detach_parasite
+           item_get_parasite
+           item_get_parasite_list);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Item procedures';
+$doc_title = 'gimpitem';
+$doc_short_desc = 'Functions to manipulate items.';
+$doc_long_desc = 'Functions to manipulate items.';
+
+1;
diff --git a/pdb/groups/item_transform.pdb b/pdb/groups/item_transform.pdb
new file mode 100644
index 0000000..694cb68
--- /dev/null
+++ b/pdb/groups/item_transform.pdb
@@ -0,0 +1,695 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+# Item Transformations
+
+# shortcuts
+
+sub transform_invoke {
+    my ($progress_text, $assemble_matrix, $check) = @_;
+    my $success_check = 'gimp_pdb_item_is_attached (item, NULL,
+                                                    GIMP_PDB_ITEM_CONTENT |
+                                                    GIMP_PDB_ITEM_POSITION, error);';
+
+    if ($check) {
+        $success_check = "(gimp_pdb_item_is_attached (item, NULL,
+                                                      GIMP_PDB_ITEM_CONTENT |
+                                                      GIMP_PDB_ITEM_POSITION, error) && " . $check . ");";
+    }
+
+    %invoke = (
+        code => <<"CODE"
+{
+  gint x, y, width, height;
+
+  success = $success_check
+
+  if (success &&
+      gimp_item_mask_intersect (item, &x, &y, &width, &height))
+    {
+      GimpPDBContext *pdb_context = GIMP_PDB_CONTEXT (context);
+      GimpMatrix3     matrix;
+      gint            off_x, off_y;
+
+      gimp_item_get_offset (item, &off_x, &off_y);
+      x += off_x;
+      y += off_y;
+
+      /* Assemble the transformation matrix */
+$assemble_matrix
+
+      if (progress)
+        gimp_progress_start (progress, FALSE, _(\"$progress_text\"));
+
+      if (GIMP_IS_DRAWABLE (item) &&
+          ! gimp_viewable_get_children (GIMP_VIEWABLE (item)) &&
+          ! gimp_channel_is_empty (gimp_image_get_mask (gimp_item_get_image (item))))
+        {
+          GimpDrawable *drawable;
+
+          drawable = gimp_drawable_transform_affine (GIMP_DRAWABLE (item),
+                                                     context, &matrix,
+                                                     pdb_context->transform_direction,
+                                                     pdb_context->interpolation,
+                                                     pdb_context->transform_resize,
+                                                     progress);
+
+          if (drawable)
+            item = GIMP_ITEM (drawable);
+          else
+            success = FALSE;
+        }
+      else
+        {
+          gimp_item_transform (item, context, &matrix,
+                               pdb_context->transform_direction,
+                               pdb_context->interpolation,
+                               pdb_context->transform_resize,
+                               progress);
+        }
+
+      if (progress)
+        gimp_progress_end (progress);
+    }
+}
+CODE
+    )
+}
+
+# The defs
+
+sub item_transform_flip_simple {
+    $blurb = <<'BLURB';
+Flip the specified item either vertically or horizontally.
+BLURB
+
+    $help = <<'HELP';
+This procedure flips the specified item. If a selection exists and the
+item is a drawable, the portion of the drawable which lies under the
+selection is cut from the drawable and made into a floating selection
+which is then flipped. If auto_center is set to TRUE, the flip is
+around the selection's center. Otherwise, the coordinate of the axis
+needs to be specified. The return value is the ID of the flipped
+item. If there was no selection or the item is not a drawable, this
+will be equal to the item ID supplied as input.  Otherwise, this will
+be the newly created and flipped drawable.
+
+This procedure is affected by the following context setters:
+gimp_context_set_transform_resize().
+HELP
+
+    &mitch_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The affected item' },
+        { name => 'flip_type',
+         type => 'enum GimpOrientationType (no GIMP_ORIENTATION_UNKNOWN)',
+          desc => 'Type of flip' },
+        { name => 'auto_center', type => 'boolean',
+          desc => 'Whether to automatically position the axis in the selection center' },
+        { name => 'axis', type => 'float',
+          desc => 'coord. of flip axis' }
+    );
+
+    @outargs = (
+       { name => 'item', type => 'item', no_declare => 1,
+         desc => 'The flipped item' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  gint x, y, width, height;
+
+  success = gimp_pdb_item_is_attached (item, NULL,
+                                       GIMP_PDB_ITEM_CONTENT |
+                                       GIMP_PDB_ITEM_POSITION, error);
+
+  if (success &&
+      gimp_item_mask_intersect (item, &x, &y, &width, &height))
+    {
+      GimpPDBContext *pdb_context = GIMP_PDB_CONTEXT (context);
+      gint            off_x, off_y;
+
+      gimp_item_get_offset (item, &off_x, &off_y);
+      x += off_x;
+      y += off_y;
+
+      gimp_transform_get_flip_axis (x, y, width, height,
+                                    flip_type, auto_center, &axis);
+
+      if (GIMP_IS_DRAWABLE (item) &&
+          ! gimp_viewable_get_children (GIMP_VIEWABLE (item)) &&
+          ! gimp_channel_is_empty (gimp_image_get_mask (gimp_item_get_image (item))))
+        {
+          GimpDrawable *drawable;
+
+          drawable = gimp_drawable_transform_flip (GIMP_DRAWABLE (item), context,
+                                                   flip_type, axis,
+                                                   pdb_context->transform_resize);
+
+          if (drawable)
+            item = GIMP_ITEM (drawable);
+          else
+            success = FALSE;
+        }
+      else
+        {
+          gimp_item_flip (item, context,
+                          flip_type, axis,
+                          pdb_context->transform_resize);
+        }
+    }
+}
+CODE
+    );
+}
+
+
+sub item_transform_flip {
+    $blurb = <<'BLURB';
+Flip the specified item around a given line.
+BLURB
+
+    $help = <<'HELP';
+This procedure flips the specified item. If a selection exists and the
+item is a drawable , the portion of the drawable which lies under the
+selection is cut from the drawable and made into a floating selection
+which is then flipped. The axis to flip around is specified by
+specifying two points from that line. The return value is the ID of
+the flipped item. If there was no selection or the item is not a
+drawable, this will be equal to the item ID supplied as input.
+Otherwise, this will be the newly created and flipped drawable.
+
+This procedure is affected by the following context setters:
+gimp_context_set_interpolation(), gimp_context_set_transform_direction(),
+gimp_context_set_transform_resize().
+HELP
+
+    &mitch_pdb_misc('2010', '2.8');
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The affected item' },
+        { name => 'x0', type => 'float',
+          desc => 'horz. coord. of one end of axis' },
+        { name => 'y0', type => 'float',
+          desc => 'vert. coord. of one end of axis' },
+        { name => 'x1', type => 'float',
+          desc => 'horz. coord. of other end of axis' },
+        { name => 'y1', type => 'float',
+          desc => 'vert. coord. of other end of axis' }
+    );
+
+    @outargs = (
+       { name => 'item', type => 'item', no_declare => 1,
+         desc => 'The flipped item' }
+    );
+
+    transform_invoke ("Flipping", <<CODE);
+      gimp_matrix3_identity (&matrix);
+      gimp_transform_matrix_flip_free (&matrix, x0, y0, x1, y1);
+CODE
+}
+
+
+sub item_transform_perspective {
+    $blurb = <<'BLURB';
+Perform a possibly non-affine transformation on the specified item.
+BLURB
+
+    $help = <<'HELP';
+This procedure performs a possibly non-affine transformation on the
+specified item by allowing the corners of the original bounding box to
+be arbitrarily remapped to any values. The specified item is remapped
+if no selection exists or it is not a drawable. However, if a
+selection exists and the item is a drawable, the portion of the
+drawable which lies under the selection is cut from the drawable and
+made into a floating selection which is then remapped as
+specified. The return value is the ID of the remapped item. If there
+was no selection or the item is not a drawable, this will be equal to
+the item ID supplied as input. Otherwise, this will be the newly
+created and remapped drawable. The 4 coordinates specify the new
+locations of each corner of the original bounding box. By specifying
+these values, any affine transformation (rotation, scaling,
+translation) can be affected. Additionally, these values can be
+specified such that the resulting transformed item will appear to
+have been projected via a perspective transform.
+
+This procedure is affected by the following context setters:
+gimp_context_set_interpolation(), gimp_context_set_transform_direction(),
+gimp_context_set_transform_resize().
+HELP
+
+    &mitch_pdb_misc('2010', '2.8');
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The affected item' },
+       { name => 'x0', type => 'float',
+         desc => 'The new x coordinate of upper-left corner of original
+                  bounding box' },
+       { name => 'y0', type => 'float',
+         desc => 'The new y coordinate of upper-left corner of original
+                  bounding box' },
+       { name => 'x1', type => 'float',
+         desc => 'The new x coordinate of upper-right corner of original
+                  bounding box' },
+       { name => 'y1', type => 'float',
+         desc => 'The new y coordinate of upper-right corner of original
+                  bounding box' },
+       { name => 'x2', type => 'float',
+         desc => 'The new x coordinate of lower-left corner of original
+                  bounding box' },
+       { name => 'y2', type => 'float',
+         desc => 'The new y coordinate of lower-left corner of original
+                  bounding box' },
+       { name => 'x3', type => 'float',
+         desc => 'The new x coordinate of lower-right corner of original
+                  bounding box' },
+       { name => 'y3', type => 'float',
+         desc => 'The new y coordinate of lower-right corner of original
+                  bounding box' }
+    );
+
+    @outargs = (
+       { name => 'item', type => 'item', no_declare => 1,
+         desc => 'The newly mapped item' }
+    );
+
+    transform_invoke ("Perspective", <<CODE);
+      gimp_matrix3_identity (&matrix);
+      gimp_transform_matrix_perspective (&matrix,
+                                         x, y, width, height,
+                                         x0, y0, x1, y1,
+                                         x2, y2, x3, y3);
+CODE
+}
+
+
+sub item_transform_rotate_simple {
+    $blurb = <<'BLURB';
+Rotate the specified item about given coordinates through the specified angle.
+BLURB
+
+    $help = <<'HELP';
+This function rotates the specified item. If a selection exists and
+the item is a drawable, the portion of the drawable which lies under
+the selection is cut from the drawable and made into a floating
+selection which is then rotated by the specified amount. The return
+value is the ID of the rotated item. If there was no selection or the
+item is not a drawable, this will be equal to the item ID supplied as
+input. Otherwise, this will be the newly created and rotated drawable.
+
+This procedure is affected by the following context setters:
+gimp_context_set_transform_resize().
+HELP
+
+    &mitch_pdb_misc('2010', '2.8');
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The affected item' },
+        { name => 'rotate_type', type => 'enum GimpRotationType',
+          desc => 'Type of rotation' },
+        { name => 'auto_center', type => 'boolean',
+          desc => 'Whether to automatically rotate around the selection center' },
+        { name => 'center_x', type => 'float',
+          desc => 'The hor. coordinate of the center of rotation' },
+        { name => 'center_y', type => 'float',
+          desc => 'The vert. coordinate of the center of rotation' }
+    );
+
+    @outargs = (
+       { name => 'item', type => 'item', no_declare => 1,
+         desc => 'The rotated item' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  gint x, y, width, height;
+
+  success = gimp_pdb_item_is_attached (item, NULL,
+                                       GIMP_PDB_ITEM_CONTENT |
+                                       GIMP_PDB_ITEM_POSITION, error);
+
+  if (success &&
+      gimp_item_mask_intersect (item, &x, &y, &width, &height))
+    {
+      GimpPDBContext *pdb_context = GIMP_PDB_CONTEXT (context);
+      gint            off_x, off_y;
+
+      gimp_item_get_offset (item, &off_x, &off_y);
+      x += off_x;
+      y += off_y;
+
+      gimp_transform_get_rotate_center (x, y, width, height,
+                                        auto_center, &center_x, &center_y);
+
+      if (GIMP_IS_DRAWABLE (item) &&
+          ! gimp_viewable_get_children (GIMP_VIEWABLE (item)) &&
+          ! gimp_channel_is_empty (gimp_image_get_mask (gimp_item_get_image (item))))
+        {
+          GimpDrawable *drawable;
+
+          drawable = gimp_drawable_transform_rotate (GIMP_DRAWABLE (item),
+                                                     context,
+                                                     rotate_type,
+                                                     center_x, center_y,
+                                                     pdb_context->transform_resize);
+
+          if (drawable)
+            item = GIMP_ITEM (drawable);
+          else
+            success = FALSE;
+        }
+      else
+        {
+          gimp_item_rotate (item, context,
+                            rotate_type,
+                            center_x, center_y,
+                            pdb_context->transform_resize);
+        }
+    }
+}
+CODE
+    );
+}
+
+
+sub item_transform_rotate {
+    $blurb = <<'BLURB';
+Rotate the specified item about given coordinates through the
+specified angle.
+BLURB
+
+    $help = <<'HELP';
+This function rotates the specified item. If a selection exists and
+the item is a drawable, the portion of the drawable which lies under
+the selection is cut from the drawable and made into a floating
+selection which is then rotated by the specified amount. The return
+value is the ID of the rotated item. If there was no selection or the
+item is not a drawable, this will be equal to the item ID supplied as
+input. Otherwise, this will be the newly created and rotated drawable.
+
+This procedure is affected by the following context setters:
+gimp_context_set_interpolation(), gimp_context_set_transform_direction(),
+gimp_context_set_transform_resize().
+HELP
+
+    &mitch_pdb_misc('2010', '2.8');
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The affected item' },
+        { name => 'angle', type => 'float',
+          desc => 'The angle of rotation (radians)' },
+        { name => 'auto_center', type => 'boolean',
+          desc => 'Whether to automatically rotate around the selection center' },
+        { name => 'center_x', type => 'float',
+          desc => 'The hor. coordinate of the center of rotation' },
+        { name => 'center_y', type => 'float',
+          desc => 'The vert. coordinate of the center of rotation' }
+    );
+
+    @outargs = (
+       { name => 'item', type => 'item', no_declare => 1,
+         desc => 'The rotated item' }
+    );
+
+    transform_invoke ("Rotating", <<CODE);
+      gimp_matrix3_identity (&matrix);
+      if (auto_center)
+        gimp_transform_matrix_rotate_rect (&matrix,
+                                           x, y, width, height, angle);
+      else
+        gimp_transform_matrix_rotate_center (&matrix,
+                                             center_x, center_y, angle);
+CODE
+}
+
+
+sub item_transform_scale {
+    $blurb = 'Scale the specified item.';
+
+    $help = <<'HELP';
+This procedure scales the specified item. If a selection exists and
+the item is a drawable, the portion of the drawable which lies under
+the selection is cut from the drawable and made into a floating
+selection which is then scaled by the specified amount. The return
+value is the ID of the scaled item. If there was no selection or the
+item is not a drawable, this will be equal to the item ID supplied as
+input. Otherwise, this will be the newly created and scaled drawable.
+
+This procedure is affected by the following context setters:
+gimp_context_set_interpolation(), gimp_context_set_transform_direction(),
+gimp_context_set_transform_resize().
+HELP
+
+    &mitch_pdb_misc('2010', '2.8');
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The affected item' },
+       { name => 'x0', type => 'float',
+         desc => 'The new x coordinate of the upper-left corner of the
+                   scaled region' },
+       { name => 'y0', type => 'float',
+         desc => 'The new y coordinate of the upper-left corner of the
+                   scaled region' },
+       { name => 'x1', type => 'float',
+         desc => 'The new x coordinate of the lower-right corner of the
+                   scaled region' },
+       { name => 'y1', type => 'float',
+         desc => 'The new y coordinate of the lower-right corner of the
+                   scaled region' }
+    );
+
+    @outargs = (
+       { name => 'item', type => 'item', no_declare => 1,
+         desc => 'The scaled item' }
+    );
+
+    transform_invoke ("Scaling", <<CODE, 'x0 < x1 && y0 < y1');
+      gimp_matrix3_identity (&matrix);
+      gimp_transform_matrix_scale (&matrix,
+                                   x, y, width, height,
+                                   x0, y0, x1 - x0, y1 - y0);
+CODE
+}
+
+
+sub item_transform_shear {
+    $blurb = <<'BLURB';
+Shear the specified item about its center by the specified magnitude.
+BLURB
+
+    $help = <<'HELP';
+This procedure shears the specified item. If a selection exists and
+the item is a drawable, the portion of the drawable which lies under
+the selection is cut from the drawable and made into a floating
+selection which is then sheard by the specified amount. The return
+value is the ID of the sheard item. If there was no selection or the
+item is not a drawable, this will be equal to the item ID supplied as
+input. Otherwise, this will be the newly created and sheard
+drawable. The shear type parameter indicates whether the shear will be
+applied horizontally or vertically. The magnitude can be either
+positive or negative and indicates the extent (in pixels) to shear by.
+
+This procedure is affected by the following context setters:
+gimp_context_set_interpolation(), gimp_context_set_transform_direction(),
+gimp_context_set_transform_resize().
+HELP
+
+    &mitch_pdb_misc('2010', '2.8');
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The affected item' },
+        { name => 'shear_type',
+         type => 'enum GimpOrientationType (no GIMP_ORIENTATION_UNKNOWN)',
+          desc => 'Type of shear' },
+        { name => 'magnitude', type => 'float',
+          desc => 'The magnitude of the shear' }
+    );
+
+    @outargs = (
+       { name => 'item', type => 'item', no_declare => 1,
+         desc => 'The sheared item' }
+    );
+
+    transform_invoke ("Shearing", <<CODE);
+      gimp_matrix3_identity (&matrix);
+      gimp_transform_matrix_shear (&matrix,
+                                   x, y, width, height,
+                                   shear_type, magnitude);
+CODE
+}
+
+
+sub item_transform_2d {
+    $blurb = 'Transform the specified item in 2d.';
+
+    $help = <<'HELP';
+This procedure transforms the specified item. If a selection exists
+and the item is a drawable, the portion of the drawable which lies
+under the selection is cut from the drawable and made into a floating
+selection which is then transformed. The transformation is done by
+scaling the image by the x and y scale factors about the point
+(source_x, source_y), then rotating around the same point, then
+translating that point to the new position (dest_x, dest_y). The
+return value is the ID of the rotated drawable. If there was no
+selection or the item is not a drawable, this will be equal to the
+item ID supplied as input. Otherwise, this will be the newly created
+and transformed drawable.
+
+This procedure is affected by the following context setters:
+gimp_context_set_interpolation(), gimp_context_set_transform_direction(),
+gimp_context_set_transform_resize().
+HELP
+
+    &mitch_pdb_misc('2010', '2.8');
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The affected item' },
+        { name => 'source_x', type => 'float',
+          desc => 'X coordinate of the transformation center' },
+        { name => 'source_y', type => 'float',
+          desc => 'Y coordinate of the transformation center' },
+        { name => 'scale_x', type => 'float',
+          desc => 'Amount to scale in x direction' },
+        { name => 'scale_y', type => 'float',
+          desc => 'Amount to scale in y direction' },
+        { name => 'angle', type => 'float',
+          desc => 'The angle of rotation (radians)' },
+        { name => 'dest_x', type => 'float',
+          desc => 'X coordinate of where the center goes' },
+        { name => 'dest_y', type => 'float',
+          desc => 'Y coordinate of where the center goes' }
+    );
+
+    @outargs = (
+       { name => 'item', type => 'item', no_declare => 1,
+         desc => 'The transformed item' }
+    );
+
+    transform_invoke ("2D Transform", <<CODE);
+      gimp_matrix3_identity  (&matrix);
+      gimp_matrix3_translate (&matrix, -source_x, -source_y);
+      gimp_matrix3_scale     (&matrix, scale_x, scale_y);
+      gimp_matrix3_rotate    (&matrix, angle);
+      gimp_matrix3_translate (&matrix, dest_x, dest_y);
+CODE
+}
+
+
+sub item_transform_matrix {
+    $blurb = 'Transform the specified item in 2d.';
+
+    $help = <<'HELP';
+This procedure transforms the specified item. If a selection exists
+and the item is a drawable, the portion of the drawable which lies
+under the selection is cut from the drawable and made into a floating
+selection which is then transformed. The transformation is done by
+assembling a 3x3 matrix from the coefficients passed.  The return
+value is the ID of the transformed item. If there was no selection or
+the item is not a drawable, this will be equal to the item ID supplied
+as input. Otherwise, this will be the newly created and transformed
+drawable.
+
+This procedure is affected by the following context setters:
+gimp_context_set_interpolation(), gimp_context_set_transform_direction(),
+gimp_context_set_transform_resize().
+HELP
+
+    &mitch_pdb_misc('2010', '2.8');
+
+    @inargs = (
+       { name => 'item', type => 'item',
+         desc => 'The affected item' },
+        { name => 'coeff_0_0', type => 'float',
+          desc => 'coefficient (0,0) of the transformation matrix' },
+        { name => 'coeff_0_1', type => 'float',
+          desc => 'coefficient (0,1) of the transformation matrix' },
+        { name => 'coeff_0_2', type => 'float',
+          desc => 'coefficient (0,2) of the transformation matrix' },
+        { name => 'coeff_1_0', type => 'float',
+          desc => 'coefficient (1,0) of the transformation matrix' },
+        { name => 'coeff_1_1', type => 'float',
+          desc => 'coefficient (1,1) of the transformation matrix' },
+        { name => 'coeff_1_2', type => 'float',
+          desc => 'coefficient (1,2) of the transformation matrix' },
+        { name => 'coeff_2_0', type => 'float',
+          desc => 'coefficient (2,0) of the transformation matrix' },
+        { name => 'coeff_2_1', type => 'float',
+          desc => 'coefficient (2,1) of the transformation matrix' },
+        { name => 'coeff_2_2', type => 'float',
+          desc => 'coefficient (2,2) of the transformation matrix' }
+    );
+
+    @outargs = (
+       { name => 'item', type => 'item', no_declare => 1,
+         desc => 'The transformed item' }
+    );
+
+    transform_invoke ("2D Transforming", <<CODE);
+      matrix.coeff[0][0] = coeff_0_0;
+      matrix.coeff[0][1] = coeff_0_1;
+      matrix.coeff[0][2] = coeff_0_2;
+      matrix.coeff[1][0] = coeff_1_0;
+      matrix.coeff[1][1] = coeff_1_1;
+      matrix.coeff[1][2] = coeff_1_2;
+      matrix.coeff[2][0] = coeff_2_0;
+      matrix.coeff[2][1] = coeff_2_1;
+      matrix.coeff[2][2] = coeff_2_2;
+CODE
+}
+
+
+@headers = qw("libgimpmath/gimpmath.h"
+              "core/gimp-transform-utils.h"
+              "core/gimpchannel.h"
+              "core/gimpdrawable.h"
+              "core/gimpdrawable-transform.h"
+              "core/gimpimage.h"
+              "core/gimpprogress.h"
+              "gimppdb-utils.h"
+              "gimppdbcontext.h"
+              "gimp-intl.h");
+
+@procs = qw(item_transform_flip_simple
+            item_transform_flip
+            item_transform_perspective
+            item_transform_rotate_simple
+            item_transform_rotate
+            item_transform_scale
+            item_transform_shear
+            item_transform_2d
+            item_transform_matrix);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Transformation procedures';
+$doc_title = 'gimpitemtransform';
+$doc_short_desc = 'Functions to perform transformations on items.';
+$doc_long_desc = 'Functions to perform transformations on items.';
+
+1;
diff --git a/pdb/groups/layer.pdb b/pdb/groups/layer.pdb
new file mode 100644
index 0000000..915c20e
--- /dev/null
+++ b/pdb/groups/layer.pdb
@@ -0,0 +1,1406 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub layer_new {
+    $blurb = 'Create a new layer.';
+
+    $help = <<'HELP';
+This procedure creates a new layer with the specified width, height,
+and type.  Name, opacity, and mode are also supplied parameters. The
+new layer still needs to be added to the image, as this is not
+automatic. Add the new layer with the gimp_image_insert_layer()
+command. Other attributes such as layer mask modes, and offsets should
+be set with explicit procedure calls.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image to which to add the layer' },
+       { name => 'width', type => '1 <= int32 <= GIMP_MAX_IMAGE_SIZE',
+         desc => 'The layer width' },
+       { name => 'height', type => '1 <= int32 <= GIMP_MAX_IMAGE_SIZE',
+         desc => 'The layer height' },
+       { name => 'type', type => 'enum GimpImageType',
+         desc => 'The layer type' },
+       { name => 'name', type => 'string',
+         desc => 'The layer name', null_ok => 1 },
+       { name => 'opacity', type => '0 <= float <= 100',
+         desc => 'The layer opacity' },
+       { name => 'mode', type => 'enum GimpLayerMode',
+          default => 'GIMP_LAYER_MODE_NORMAL',
+         desc => 'The layer combination mode' }
+    );
+
+    @outargs = (
+       { name => 'layer', type => 'layer', wrap => 1,
+         desc => 'The newly created layer' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpImageBaseType  base_type = GIMP_RGB;
+  gboolean           has_alpha = FALSE;
+  const Babl        *format;
+
+  if (mode == GIMP_LAYER_MODE_OVERLAY_LEGACY)
+    mode = GIMP_LAYER_MODE_SOFTLIGHT_LEGACY;
+
+  switch (type)
+    {
+    case GIMP_RGB_IMAGE:
+      base_type = GIMP_RGB;
+      has_alpha = FALSE;
+      break;
+
+    case GIMP_RGBA_IMAGE:
+      base_type = GIMP_RGB;
+      has_alpha = TRUE;
+      break;
+
+    case GIMP_GRAY_IMAGE:
+      base_type = GIMP_GRAY;
+      has_alpha = FALSE;
+      break;
+
+    case GIMP_GRAYA_IMAGE:
+      base_type = GIMP_GRAY;
+      has_alpha = TRUE;
+      break;
+
+    case GIMP_INDEXED_IMAGE:
+      base_type = GIMP_INDEXED;
+      has_alpha = FALSE;
+      break;
+
+    case GIMP_INDEXEDA_IMAGE:
+      base_type = GIMP_INDEXED;
+      has_alpha = TRUE;
+      break;
+    }
+
+  /* do not use gimp_image_get_layer_format() because it might
+   * be the floating selection of a channel or mask
+   */
+  format = gimp_image_get_format (image, base_type,
+                                  gimp_image_get_precision (image),
+                                  has_alpha);
+
+  layer = gimp_layer_new (image, width, height,
+                          format, name, opacity / 100.0, mode);
+
+  if (! layer)
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub layer_new_from_visible {
+    $blurb = 'Create a new layer from what is visible in an image.';
+
+    $help = <<'HELP';
+This procedure creates a new layer from what is visible in the given
+image.  The new layer still needs to be added to the destination
+image, as this is not automatic. Add the new layer with the
+gimp_image_insert_layer() command.  Other attributes such as layer
+mask modes, and offsets should be set with explicit procedure calls.
+HELP
+
+    &neo_pdb_misc('2008', '2.6');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The source image from where the content is copied' },
+       { name => 'dest_image', type => 'image',
+         desc => 'The destination image to which to add the layer' },
+       { name => 'name', type => 'string',
+         desc => 'The layer name', null_ok => 1 }
+     );
+
+    @outargs = (
+       { name => 'layer', type => 'layer',
+         desc => 'The newly created layer' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPickable     *pickable = GIMP_PICKABLE (image);
+  GimpColorProfile *profile;
+
+  gimp_pickable_flush (pickable);
+
+  profile = gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (image));
+
+  layer = gimp_layer_new_from_gegl_buffer (gimp_pickable_get_buffer (pickable),
+                                           dest_image,
+                                           gimp_image_get_layer_format (dest_image,
+                                                                        TRUE),
+                                           name,
+                                           GIMP_OPACITY_OPAQUE,
+                                           gimp_image_get_default_new_layer_mode (dest_image),
+                                           profile);
+}
+CODE
+    );
+}
+
+sub layer_new_from_drawable {
+    $blurb = 'Create a new layer by copying an existing drawable.';
+
+    $help = <<'HELP';
+This procedure creates a new layer as a copy of the specified
+drawable.  The new layer still needs to be added to the image, as this
+is not automatic.  Add the new layer with the
+gimp_image_insert_layer() command. Other attributes such as layer mask
+modes, and offsets should be set with explicit procedure calls.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The source drawable from where the new layer is copied' },
+       { name => 'dest_image', type => 'image',
+         desc => 'The destination image to which to add the layer' }
+     );
+
+    @outargs = (
+       { name => 'layer_copy', type => 'layer',
+         desc => 'The newly copied layer' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GType     new_type;
+  GimpItem *new_item;
+
+  if (GIMP_IS_LAYER (drawable))
+    new_type = G_TYPE_FROM_INSTANCE (drawable);
+  else
+    new_type = GIMP_TYPE_LAYER;
+
+  new_item = gimp_item_convert (GIMP_ITEM (drawable), dest_image, new_type);
+
+  if (new_item)
+    layer_copy = GIMP_LAYER (new_item);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub layer_group_new {
+    $blurb = 'Create a new layer group.';
+
+    $help = <<'HELP';
+This procedure creates a new layer group. Attributes such as layer mode
+and opacity should be set with explicit procedure calls. Add the new
+layer group (which is a kind of layer) with the
+gimp_image_insert_layer() command.
+
+Other procedures useful with layer groups: gimp_image_reorder_item(),
+gimp_item_get_parent(), gimp_item_get_children(), gimp_item_is_group().
+HELP
+
+    &barak_pdb_misc('2010', '2.8');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image to which to add the layer group' }
+    );
+
+    @outargs = (
+       { name => 'layer_group', type => 'layer',
+         desc => 'The newly created layer group' }
+    );
+    %invoke = (
+       code => <<'CODE'
+{
+  layer_group = gimp_group_layer_new (image);
+
+  if (! layer_group)
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub layer_copy {
+    $blurb = 'Copy a layer.';
+
+    $help = <<'HELP';
+This procedure copies the specified layer and returns the copy. The newly
+copied layer is for use within the original layer's image. It should not be
+subsequently added to any other image. The copied layer can optionally have an
+added alpha channel. This is useful if the background layer in an image is
+being copied and added to the same image.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'layer', type => 'layer',
+         desc => 'The layer to copy' },
+       { name => 'add_alpha', type => 'boolean',
+         desc => 'Add an alpha channel to the copied layer' }
+    );
+
+    @outargs = (
+       { name => 'layer_copy', type => 'layer', wrap => 1,
+         desc => 'The newly copied layer' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  layer_copy = GIMP_LAYER (gimp_item_duplicate (GIMP_ITEM (layer),
+                                                G_TYPE_FROM_INSTANCE (layer)));
+  if (layer_copy)
+    {
+      if (add_alpha)
+        gimp_layer_add_alpha (layer_copy);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub layer_create_mask {
+    $blurb = 'Create a layer mask for the specified layer.';
+
+    $help = <<'HELP';
+This procedure creates a layer mask for the specified layer.
+
+Layer masks serve as an additional alpha channel for a layer. Different
+types of masks are allowed for initialisation:
+
+- white mask (leaves the layer fully visible);
+
+- black mask (gives the layer complete transparency);
+
+- the layer's alpha channel (either a copy, or a transfer, which leaves
+the layer fully visible, but which may be more useful than a white mask);
+
+- the current selection;
+
+- a grayscale copy of the layer;
+
+- or a copy of the active channel.
+
+
+The layer mask still needs to be added to the layer. This can be done
+with a call to gimp_layer_add_mask().
+
+
+gimp_layer_create_mask() will fail if there are no active channels on
+the image, when called with 'ADD-CHANNEL-MASK'. It will return a black
+mask when called with 'ADD-ALPHA-MASK' or 'ADD-ALPHA-TRANSFER-MASK' on
+a layer with no alpha channels, or with 'ADD-SELECTION-MASK' when there
+is no selection on the image.
+HELP
+
+    &std_pdb_misc();
+
+    @inargs = (
+       { name => 'layer', type => 'layer',
+         desc => 'The layer to which to add the mask' },
+       { name => 'mask_type', type => 'enum GimpAddMaskType',
+         desc => 'The type of mask' }
+    );
+
+    @outargs = (
+       { name => 'mask', type => 'layer_mask',
+         desc => 'The newly created mask' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpChannel *channel = NULL;
+
+  if (mask_type == GIMP_ADD_MASK_CHANNEL)
+    {
+      channel = gimp_image_get_active_channel (gimp_item_get_image (GIMP_ITEM (layer)));
+
+      if (! channel)
+        success = FALSE;
+    }
+
+  if (success)
+    {
+      mask = gimp_layer_create_mask (layer, mask_type, channel);
+
+      if (! mask)
+        success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub layer_add_mask {
+    $blurb = 'Add a layer mask to the specified layer.';
+
+    $help = <<'HELP';
+This procedure adds a layer mask to the specified layer. Layer masks serve as
+an additional alpha channel for a layer. This procedure will fail if a number
+of prerequisites aren't met. The layer cannot already have a layer mask. The
+specified mask must exist and have the same dimensions as the layer. The layer must have been created for 
use with the specified image and the mask must have been created with the procedure 'gimp-layer-create-mask'.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'layer', type => 'layer',
+         desc => 'The layer to receive the mask' },
+       { name => 'mask', type => 'layer_mask',
+         desc => 'The mask to add to the layer' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_floating (GIMP_ITEM (mask),
+                                 gimp_item_get_image (GIMP_ITEM (layer)),
+                                 error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (layer), error))
+    success = (gimp_layer_add_mask (layer, mask, TRUE, error) == mask);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub layer_remove_mask {
+    $blurb = 'Remove the specified layer mask from the layer.';
+
+    $help = <<'HELP';
+This procedure removes the specified layer mask from the layer. If the mask
+doesn't exist, an error is returned.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'layer', type => 'layer',
+         desc => 'The layer from which to remove mask' },
+       { name => 'mode', type => 'enum GimpMaskApplyMode',
+         desc => 'Removal mode' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPDBItemModify modify = 0;
+
+  if (mode == GIMP_MASK_APPLY)
+    modify |= GIMP_PDB_ITEM_CONTENT;
+
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (layer), NULL, modify, error) &&
+      gimp_layer_get_mask (layer))
+    gimp_layer_apply_mask (layer, mode, TRUE);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub layer_scale {
+    $blurb = 'Scale the layer using the default interpolation method.';
+
+    $help = <<'HELP';
+This procedure scales the layer so that its new width and height are
+equal to the supplied parameters. The 'local-origin' parameter
+specifies whether to scale from the center of the layer, or from the
+image origin. This operation only works if the layer has been added to
+an image. The interpolation method used can be set with
+gimp_context_set_interpolation().
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'layer', type => 'layer',
+         desc => 'The layer' },
+       { name => 'new_width', type => '1 <= int32 <= GIMP_MAX_IMAGE_SIZE',
+         desc => 'New layer width' },
+       { name => 'new_height', type => '1 <= int32 <= GIMP_MAX_IMAGE_SIZE',
+         desc => 'New layer height' },
+       { name => 'local_origin', type => 'boolean',
+         desc => 'Use a local origin (as opposed to the image origin)' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (layer), NULL,
+                                 GIMP_PDB_ITEM_CONTENT | GIMP_PDB_ITEM_POSITION,
+                                 error))
+    {
+      GimpPDBContext *pdb_context = GIMP_PDB_CONTEXT (context);
+
+      if (progress)
+        gimp_progress_start (progress, FALSE, _("Scaling"));
+
+      gimp_item_scale_by_origin (GIMP_ITEM (layer), new_width, new_height,
+                                 pdb_context->interpolation, progress,
+                                 local_origin);
+
+      if (progress)
+        gimp_progress_end (progress);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub layer_scale_full {
+    &std_pdb_deprecated('gimp-layer-scale');
+    &neo_pdb_misc('2008', '2.6');
+
+    @inargs = (
+       { name => 'layer', type => 'layer',
+         desc => 'The layer' },
+       { name => 'new_width', type => '1 <= int32 <= GIMP_MAX_IMAGE_SIZE',
+         desc => 'New layer width' },
+       { name => 'new_height', type => '1 <= int32 <= GIMP_MAX_IMAGE_SIZE',
+         desc => 'New layer height' },
+       { name => 'local_origin', type => 'boolean',
+         desc => 'Use a local origin (as opposed to the image origin)' },
+       { name => 'interpolation', type => 'enum GimpInterpolationType',
+         desc => 'Type of interpolation' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (layer), NULL,
+                                 GIMP_PDB_ITEM_CONTENT | GIMP_PDB_ITEM_POSITION,
+                                 error))
+    {
+      if (progress)
+        gimp_progress_start (progress, FALSE, _("Scaling"));
+
+      gimp_item_scale_by_origin (GIMP_ITEM (layer), new_width, new_height,
+                                 interpolation, progress,
+                                 local_origin);
+
+      if (progress)
+        gimp_progress_end (progress);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub layer_resize {
+    $blurb = 'Resize the layer to the specified extents.';
+
+    $help = <<'HELP';
+This procedure resizes the layer so that its new width and height are
+equal to the supplied parameters. Offsets are also provided which
+describe the position of the previous layer's content. This operation
+only works if the layer has been added to an image.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'layer', type => 'layer',
+         desc => 'The layer' },
+       { name => 'new_width', type => '1 <= int32 <= GIMP_MAX_IMAGE_SIZE',
+         desc => 'New layer width' },
+       { name => 'new_height', type => '1 <= int32 <= GIMP_MAX_IMAGE_SIZE',
+         desc => 'New layer height' },
+       { name => 'offx', type => 'int32',
+         desc => 'x offset between upper left corner of old and
+                  new layers: (old - new)' },
+       { name => 'offy', type => 'int32',
+         desc => 'y offset between upper left corner of old and
+                  new layers: (old - new)' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (layer), NULL,
+                                 GIMP_PDB_ITEM_CONTENT | GIMP_PDB_ITEM_POSITION,
+                                 error))
+    gimp_item_resize (GIMP_ITEM (layer), context, GIMP_FILL_TRANSPARENT,
+                      new_width, new_height, offx, offy);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub layer_resize_to_image_size {
+    $blurb = 'Resize a layer to the image size.';
+
+    $help = <<'HELP';
+This procedure resizes the layer so that it's new width and height are equal to
+the width and height of its image container.
+HELP
+
+    &yosh_pdb_misc('2003');
+
+    @inargs = (
+       { name => 'layer', type => 'layer',
+         desc => 'The layer to resize' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (layer), NULL,
+                                 GIMP_PDB_ITEM_CONTENT | GIMP_PDB_ITEM_POSITION,
+                                 error))
+    gimp_layer_resize_to_image (layer, context, GIMP_FILL_TRANSPARENT);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub layer_translate {
+    $blurb = 'Translate the layer by the specified offsets.';
+
+    $help = <<'HELP';
+This procedure translates the layer by the amounts specified in the x and y
+arguments. These can be negative, and are considered offsets from the current
+position. This command only works if the layer has been added to an image. All
+additional layers contained in the image which have the linked flag set to TRUE
+w ill also be translated by the specified offsets.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'layer', type => 'layer',
+         desc => 'The layer' },
+       { name => 'offx', type => 'int32',
+         desc => "Offset in x direction" },
+       { name => 'offy', type => 'int32',
+         desc => "Offset in y direction" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_modifyable (GIMP_ITEM (layer),
+                                   GIMP_PDB_ITEM_POSITION, error))
+    {
+      if (gimp_item_get_linked (GIMP_ITEM (layer)))
+        {
+          gimp_item_linked_translate (GIMP_ITEM (layer), offx, offy, TRUE);
+        }
+      else
+        {
+          gimp_item_translate (GIMP_ITEM (layer), offx, offy, TRUE);
+        }
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub layer_add_alpha {
+    $blurb = <<'BLURB';
+Add an alpha channel to the layer if it doesn't already have one.
+BLURB
+
+    $help = <<'HELP';
+This procedure adds an additional component to the specified layer if
+it does not already possess an alpha channel. An alpha channel makes
+it possible to clear and erase to transparency, instead of the
+background color. This transforms layers of type RGB to RGBA, GRAY to
+GRAYA, and INDEXED to INDEXEDA.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'layer', type => 'layer',
+         desc => 'The layer' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_modifyable (GIMP_ITEM (layer),
+                                   GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (layer), error))
+    {
+      gimp_layer_add_alpha (layer);
+    }
+  else
+   success = FALSE;
+}
+CODE
+    );
+}
+
+sub layer_flatten {
+    $blurb = <<'BLURB';
+Remove the alpha channel from the layer if it has one.
+BLURB
+
+    $help = <<'HELP';
+This procedure removes the alpha channel from a layer, blending all
+(partially) transparent pixels in the layer against the background
+color. This transforms layers of type RGBA to RGB, GRAYA to
+GRAY, and INDEXEDA to INDEXED.
+HELP
+
+    &mitch_pdb_misc('2007', '2.4');
+
+    @inargs = (
+       { name => 'layer', type => 'layer',
+         desc => 'The layer' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_modifyable (GIMP_ITEM (layer),
+                                   GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (layer), error))
+    {
+      gimp_layer_remove_alpha (layer, context);
+    }
+  else
+   success = FALSE;
+}
+CODE
+    );
+}
+
+sub layer_set_offsets {
+    $blurb = 'Set the layer offsets.';
+
+    $help = <<'HELP';
+This procedure sets the offsets for the specified layer. The offsets are
+relative to the image origin and can be any values. This operation is valid
+only on layers which have been added to an image.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'layer', type => 'layer',
+         desc => 'The layer' },
+       { name => 'offx', type => 'int32',
+         desc => "Offset in x direction" },
+       { name => 'offy', type => 'int32',
+         desc => "Offset in y direction" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_modifyable (GIMP_ITEM (layer),
+                                   GIMP_PDB_ITEM_POSITION, error))
+    {
+      gint offset_x;
+      gint offset_y;
+
+      gimp_item_get_offset (GIMP_ITEM (layer), &offset_x, &offset_y);
+      offx -= offset_x;
+      offy -= offset_y;
+
+      if (gimp_item_get_linked (GIMP_ITEM (layer)))
+        {
+          gimp_item_linked_translate (GIMP_ITEM (layer), offx, offy, TRUE);
+        }
+      else
+        {
+          gimp_item_translate (GIMP_ITEM (layer), offx, offy, TRUE);
+        }
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub layer_get_mask {
+    $blurb = "Get the specified layer's mask if it exists.";
+
+    $help = <<'HELP';
+This procedure returns the specified layer's mask, or -1 if none exists.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'layer', type => 'layer',
+         desc => 'The layer' }
+    );
+
+    @outargs = (
+       { name => 'mask', type => 'layer_mask',
+         desc => 'The layer mask',
+         return_fail => -1 }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  mask = gimp_layer_get_mask (layer);
+}
+CODE
+    );
+}
+
+sub layer_from_mask {
+    $blurb = "Get the specified mask's layer.";
+
+    $help = <<'HELP';
+This procedure returns the specified mask's layer , or -1 if none exists.
+HELP
+
+    $author = $copyright = 'Geert Jordaens';
+    $date = '2004';
+    $since = '2.2';
+
+    @inargs = (
+        { name => 'mask', type => 'layer_mask',
+          desc => 'Mask for which to return the layer' }
+    );
+
+    @outargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The mask\'s layer',
+         return_fail => -1 }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  layer = gimp_layer_mask_get_layer (mask);
+}
+CODE
+    );
+}
+
+sub layer_is_floating_sel {
+    $blurb = 'Is the specified layer a floating selection?';
+
+    $help = <<'HELP';
+This procedure returns whether the layer is a floating selection. Floating
+selections are special cases of layers which are attached to a specific
+drawable.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'layer', type => 'layer',
+         desc => 'The layer' }
+    );
+
+    @outargs = (
+       { name => 'is_floating_sel', type => 'boolean',
+         desc => 'TRUE if the layer is a floating selection' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  is_floating_sel = gimp_layer_is_floating_sel (layer);
+}
+CODE
+    );
+}
+
+sub layer_get_lock_alpha {
+    $blurb = 'Get the lock alpha channel setting of the specified layer.';
+
+    $help = <<'HELP';
+This procedure returns the specified layer's lock alpha channel setting.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'layer', type => 'layer',
+         desc => 'The layer' }
+    );
+
+    @outargs = (
+       { name => 'lock_alpha', type => 'boolean',
+         desc => 'The layer\'s lock alpha channel setting' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  lock_alpha = gimp_layer_get_lock_alpha (layer);
+}
+CODE
+    );
+}
+
+sub layer_set_lock_alpha {
+    $blurb = 'Set the lock alpha channel setting of the specified layer.';
+
+    $help = <<'HELP';
+This procedure sets the specified layer's lock alpha channel setting.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'layer', type => 'layer',
+         desc => 'The layer' },
+       { name => 'lock_alpha', type => 'boolean',
+         desc => 'The new layer\'s lock alpha channel setting' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_layer_can_lock_alpha (layer))
+    gimp_layer_set_lock_alpha (layer, lock_alpha, TRUE);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub layer_get_apply_mask {
+    $blurb = 'Get the apply mask setting of the specified layer.';
+
+    $help = <<'HELP';
+This procedure returns the specified layer's apply mask setting. If
+the value is TRUE, then the layer mask for this layer is currently
+being composited with the layer's alpha channel.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'layer', type => 'layer',
+         desc => 'The layer' }
+    );
+
+    @outargs = (
+       { name => 'apply_mask', type => 'boolean',
+         desc => 'The layer\'s apply mask setting' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (layer->mask)
+    apply_mask = gimp_layer_get_apply_mask (layer);
+  else
+    apply_mask = FALSE;
+}
+CODE
+    );
+}
+
+sub layer_set_apply_mask {
+    $blurb = 'Set the apply mask setting of the specified layer.';
+
+    $help = <<'HELP';
+This procedure sets the specified layer's apply mask setting. This
+controls whether the layer's mask is currently affecting the alpha
+channel. If there is no layer mask, this function will return an
+error.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'layer', type => 'layer',
+         desc => 'The layer' },
+       { name => 'apply_mask', type => 'boolean',
+         desc => 'The new layer\'s apply mask setting' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (layer->mask)
+    gimp_layer_set_apply_mask (layer, apply_mask, TRUE);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub layer_get_show_mask {
+    $blurb = 'Get the show mask setting of the specified layer.';
+
+    $help = <<'HELP';
+This procedure returns the specified layer's show mask setting. This
+controls whether the layer or its mask is visible. TRUE indicates
+that the mask should be visible. If the layer has no mask,
+then this function returns an error.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'layer', type => 'layer',
+         desc => 'The layer' }
+    );
+
+    @outargs = (
+       { name => 'show_mask', type => 'boolean',
+         desc => 'The layer\'s show mask setting' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (layer->mask)
+    show_mask = gimp_layer_get_show_mask (layer);
+  else
+    show_mask = FALSE;
+}
+CODE
+    );
+}
+
+sub layer_set_show_mask {
+    $blurb = 'Set the show mask setting of the specified layer.';
+
+    $help = <<'HELP';
+This procedure sets the specified layer's show mask setting. This
+controls whether the layer or its mask is visible. TRUE indicates that
+the mask should be visible. If there is no layer mask, this function
+will return an error.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'layer', type => 'layer',
+         desc => 'The layer' },
+       { name => 'show_mask', type => 'boolean',
+         desc => 'The new layer\'s show mask setting' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (layer->mask)
+    gimp_layer_set_show_mask (layer, show_mask, TRUE);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub layer_get_edit_mask {
+    $blurb = 'Get the edit mask setting of the specified layer.';
+
+    $help = <<'HELP';
+This procedure returns the specified layer's edit mask setting. If
+the value is TRUE, then the layer mask for this layer is currently
+active, and not the layer.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'layer', type => 'layer',
+         desc => 'The layer' }
+    );
+
+    @outargs = (
+       { name => 'edit_mask', type => 'boolean',
+         desc => 'The layer\'s edit mask setting' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (layer->mask)
+    edit_mask = gimp_layer_get_edit_mask (layer);
+  else
+    edit_mask = FALSE;
+}
+CODE
+    );
+}
+
+sub layer_set_edit_mask {
+    $blurb = 'Set the edit mask setting of the specified layer.';
+
+    $help = <<'HELP';
+This procedure sets the specified layer's edit mask setting. This
+controls whether the layer or it's mask is currently active for
+editing.  If the specified layer has no layer mask, then this
+procedure will return an error.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'layer', type => 'layer',
+         desc => 'The layer' },
+       { name => 'edit_mask', type => 'boolean',
+         desc => 'The new layer\'s edit mask setting' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (layer->mask)
+    gimp_layer_set_edit_mask (layer, edit_mask);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub layer_get_opacity {
+    $blurb = 'Get the opacity of the specified layer.';
+    $help  = "This procedure returns the specified layer's opacity.";
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'layer', type => 'layer',
+         desc => 'The layer' }
+    );
+
+    @outargs = (
+       { name => 'opacity', type => '0 <= float <= 100',
+         desc => 'The layer opacity' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  opacity = gimp_layer_get_opacity (layer) * 100.0;
+}
+CODE
+    );
+}
+
+sub layer_set_opacity {
+    $blurb = 'Set the opacity of the specified layer.';
+    $help  = "This procedure sets the specified layer's opacity.";
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'layer', type => 'layer',
+         desc => 'The layer' },
+       { name => 'opacity', type => '0 <= float <= 100',
+         desc => 'The new layer opacity' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_layer_set_opacity (layer, opacity / 100.0, TRUE);
+}
+CODE
+    );
+}
+
+sub layer_get_mode {
+    $blurb = 'Get the combination mode of the specified layer.';
+    $help  = "This procedure returns the specified layer's combination mode.";
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'layer', type => 'layer',
+         desc => 'The layer' }
+    );
+
+    @outargs = (
+       { name => 'mode', type => 'enum GimpLayerMode',
+          default => 'GIMP_LAYER_MODE_NORMAL',
+         desc => 'The layer combination mode' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  mode = gimp_layer_get_mode (layer);
+}
+CODE
+    );
+}
+
+sub layer_set_mode {
+    $blurb = 'Set the combination mode of the specified layer.';
+    $help  = "This procedure sets the specified layer's combination mode.";
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'layer', type => 'layer',
+         desc => 'The layer' },
+       { name => 'mode', type => 'enum GimpLayerMode',
+          default => 'GIMP_LAYER_MODE_NORMAL',
+         desc => 'The new layer combination mode' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (mode == GIMP_LAYER_MODE_OVERLAY_LEGACY)
+    mode = GIMP_LAYER_MODE_SOFTLIGHT_LEGACY;
+
+  if (gimp_viewable_get_children (GIMP_VIEWABLE (layer)) == NULL)
+    {
+      if (! (gimp_layer_mode_get_context (mode) & GIMP_LAYER_MODE_CONTEXT_LAYER))
+        success = FALSE;
+    }
+  else
+    {
+      if (! (gimp_layer_mode_get_context (mode) & GIMP_LAYER_MODE_CONTEXT_GROUP))
+        success = FALSE;
+    }
+
+  if (success)
+    gimp_layer_set_mode (layer, mode, TRUE);
+}
+CODE
+    );
+}
+
+sub layer_get_blend_space {
+    $blurb = 'Get the blend space of the specified layer.';
+    $help  = "This procedure returns the specified layer's blend space.";
+
+    &ell_pdb_misc('2017', '2.10');
+
+    @inargs = (
+       { name => 'layer', type => 'layer',
+         desc => 'The layer' }
+    );
+
+    @outargs = (
+       { name => 'blend_space', type => 'enum GimpLayerColorSpace',
+         desc => 'The layer blend space' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  blend_space = gimp_layer_get_blend_space (layer);
+}
+CODE
+    );
+}
+
+sub layer_set_blend_space {
+    $blurb = 'Set the blend space of the specified layer.';
+    $help  = "This procedure sets the specified layer's blend space.";
+
+    &ell_pdb_misc('2017', '2.10');
+
+    @inargs = (
+       { name => 'layer', type => 'layer',
+         desc => 'The layer' },
+       { name => 'blend_space', type => 'enum GimpLayerColorSpace',
+         desc => 'The new layer blend space' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_layer_set_blend_space (layer, blend_space, TRUE);
+}
+CODE
+    );
+}
+
+sub layer_get_composite_space {
+    $blurb = 'Get the composite space of the specified layer.';
+    $help  = "This procedure returns the specified layer's composite space.";
+
+    &ell_pdb_misc('2017', '2.10');
+
+    @inargs = (
+       { name => 'layer', type => 'layer',
+         desc => 'The layer' }
+    );
+
+    @outargs = (
+       { name => 'composite_space', type => 'enum GimpLayerColorSpace',
+         desc => 'The layer composite space' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  composite_space = gimp_layer_get_composite_space (layer);
+}
+CODE
+    );
+}
+
+sub layer_set_composite_space {
+    $blurb = 'Set the composite space of the specified layer.';
+    $help  = "This procedure sets the specified layer's composite space.";
+
+    &ell_pdb_misc('2017', '2.10');
+
+    @inargs = (
+       { name => 'layer', type => 'layer',
+         desc => 'The layer' },
+       { name => 'composite_space', type => 'enum GimpLayerColorSpace',
+         desc => 'The new layer composite space' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_layer_set_composite_space (layer, composite_space, TRUE);
+}
+CODE
+    );
+}
+
+sub layer_get_composite_mode {
+    $blurb = 'Get the composite mode of the specified layer.';
+    $help  = "This procedure returns the specified layer's composite mode.";
+
+    &ell_pdb_misc('2017', '2.10');
+
+    @inargs = (
+       { name => 'layer', type => 'layer',
+         desc => 'The layer' }
+    );
+
+    @outargs = (
+       { name => 'composite_mode', type => 'enum GimpLayerCompositeMode',
+         desc => 'The layer composite mode' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  composite_mode = gimp_layer_get_composite_mode (layer);
+}
+CODE
+    );
+}
+
+sub layer_set_composite_mode {
+    $blurb = 'Set the composite mode of the specified layer.';
+    $help  = "This procedure sets the specified layer's composite mode.";
+
+    &ell_pdb_misc('2017', '2.10');
+
+    @inargs = (
+       { name => 'layer', type => 'layer',
+         desc => 'The layer' },
+       { name => 'composite_mode', type => 'enum GimpLayerCompositeMode',
+         desc => 'The new layer composite mode' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_layer_set_composite_mode (layer, composite_mode, TRUE);
+}
+CODE
+    );
+}
+
+@headers = qw(<cairo.h>
+              "libgimpbase/gimpbase.h"
+              "libgimpcolor/gimpcolor.h"
+              "core/gimp.h"
+              "core/gimpimage-color-profile.h"
+              "core/gimpimage-undo.h"
+              "core/gimpitem-linked.h"
+              "core/gimpgrouplayer.h"
+              "core/gimplayer-new.h"
+              "core/gimppickable.h"
+              "core/gimpprogress.h"
+              "operations/layer-modes/gimp-layer-modes.h"
+              "gimppdbcontext.h"
+              "gimppdb-utils.h"
+              "gimp-intl.h");
+
+@procs = qw(layer_new
+            layer_new_from_visible
+            layer_new_from_drawable
+            layer_group_new
+            layer_copy
+            layer_add_alpha
+            layer_flatten
+            layer_scale layer_scale_full
+            layer_resize layer_resize_to_image_size
+           layer_translate
+            layer_set_offsets
+            layer_create_mask
+            layer_get_mask
+            layer_from_mask
+            layer_add_mask layer_remove_mask
+           layer_is_floating_sel
+            layer_get_lock_alpha layer_set_lock_alpha
+            layer_get_apply_mask layer_set_apply_mask
+            layer_get_show_mask layer_set_show_mask
+            layer_get_edit_mask layer_set_edit_mask
+            layer_get_opacity layer_set_opacity
+            layer_get_mode layer_set_mode
+            layer_get_blend_space layer_set_blend_space
+            layer_get_composite_space layer_set_composite_space
+            layer_get_composite_mode layer_set_composite_mode);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Layer';
+$doc_title = 'gimplayer';
+$doc_short_desc = 'Operations on a single layer.';
+$doc_long_desc = 'Operations on a single layer.';
+
+1;
diff --git a/pdb/groups/message.pdb b/pdb/groups/message.pdb
new file mode 100644
index 0000000..7ccbc34
--- /dev/null
+++ b/pdb/groups/message.pdb
@@ -0,0 +1,116 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1998-1999 Manish Singh
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub message {
+    $blurb = 'Displays a dialog box with a message.';
+
+    $help = <<'HELP';
+Displays a dialog box with a message. Useful for status or error reporting.
+The message must be in UTF-8 encoding.
+HELP
+
+    &yosh_pdb_misc('1998');
+
+    @inargs = (
+       { name => 'message', type => 'string',
+         desc => 'Message to display in the dialog' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  const gchar *domain = NULL;
+
+  if (gimp->plug_in_manager->current_plug_in)
+    domain = gimp_plug_in_get_undo_desc (gimp->plug_in_manager->current_plug_in);
+  gimp_show_message (gimp, G_OBJECT (progress), GIMP_MESSAGE_WARNING,
+                     domain, message);
+}
+CODE
+    );
+}
+
+sub message_get_handler {
+    $blurb = <<'BLURB';
+Returns the current state of where warning messages are displayed.
+BLURB
+
+    $help = <<'HELP';
+This procedure returns the way g_message warnings are displayed. They can be
+shown in a dialog box or printed on the console where gimp was started.
+HELP
+
+    &yosh_pdb_misc('1998');
+
+    @outargs = (
+       { name => 'handler', type => 'enum GimpMessageHandlerType',
+         desc => 'The current handler type' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  handler = gimp->message_handler;
+}
+CODE
+    );
+}
+
+sub message_set_handler {
+    $blurb = 'Controls where warning messages are displayed.';
+
+    $help = <<'HELP';
+This procedure controls how g_message warnings are displayed. They can be shown
+in a dialog box or printed on the console where gimp was started.
+HELP
+
+    &yosh_pdb_misc('1998');
+
+    @inargs = (
+       { name => 'handler', type => 'enum GimpMessageHandlerType',
+         desc => 'The new handler type' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp->message_handler = handler;
+}
+CODE
+    );
+}
+
+
+@headers = qw(<string.h>
+              "core/gimp.h"
+              "plug-in/gimpplugin.h"
+              "plug-in/gimppluginmanager.h"
+              "gimp-intl.h");
+
+@procs = qw(message
+            message_get_handler
+            message_set_handler);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Message procedures';
+$doc_title = 'gimpmessage';
+$doc_short_desc = 'Display a dialog box with a message.';
+$doc_long_desc = 'Display a dialog box with a message.';
+
+1;
diff --git a/pdb/groups/paint_tools.pdb b/pdb/groups/paint_tools.pdb
new file mode 100644
index 0000000..593bd4e
--- /dev/null
+++ b/pdb/groups/paint_tools.pdb
@@ -0,0 +1,1071 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub stroke_arg () {
+    { name => 'strokes', type => 'floatarray',
+      desc => 'Array of stroke coordinates: { s1.x, s1.y, s2.x, s2.y, ...,
+              sn.x, sn.y }',
+      array => { type => '2 <= int32',
+                desc => 'Number of stroke control points (count each
+                         coordinate as 2 points)' } }
+}
+
+# The defs
+
+sub airbrush {
+    $blurb = <<'BLURB';
+Paint in the current brush with varying pressure. Paint application is
+time-dependent.
+BLURB
+
+    $help = <<'HELP';
+This tool simulates the use of an airbrush. Paint pressure represents the
+relative intensity of the paint application. High pressure results in a thicker
+layer of paint while low pressure results in a thinner layer.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+       { name => 'pressure', type => '0 <= float <= 100',
+         desc => 'The pressure of the airbrush strokes' },
+       &stroke_arg
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-airbrush");
+
+  if (options &&
+      gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      options = gimp_config_duplicate (GIMP_CONFIG (options));
+
+      g_object_set (options,
+                    "pressure", pressure,
+                    NULL);
+
+      success = paint_tools_stroke (gimp, context, options, drawable,
+                                    num_strokes, strokes, error,
+                                   "undo-desc", options->paint_info->blurb,
+                                   NULL);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub airbrush_default {
+    $blurb = <<'BLURB';
+Paint in the current brush with varying pressure. Paint application is
+time-dependent.
+BLURB
+
+    $help = <<'HELP';
+This tool simulates the use of an airbrush. It is similar to gimp_airbrush()
+except that the pressure is derived from the airbrush tools options box.
+It the option has not been set the default for the option will be used.
+HELP
+
+    &andy_pdb_misc('1999');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+       &stroke_arg
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-airbrush");
+
+  if (options &&
+      gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      options = gimp_config_duplicate (GIMP_CONFIG (options));
+
+      success = paint_tools_stroke (gimp, context, options, drawable,
+                                    num_strokes, strokes, error,
+                                   "undo-desc", options->paint_info->blurb,
+                                   NULL);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+sub clone {
+    $blurb = <<'BLURB';
+Clone from the source to the dest drawable using the current brush
+BLURB
+
+    $help = <<'HELP';
+This tool clones (copies) from the source drawable starting at the specified
+source coordinates to the dest drawable. If the "clone_type" argument is set
+to PATTERN-CLONE, then the current pattern is used as the source and the
+"src_drawable" argument is ignored. Pattern cloning assumes a tileable
+pattern and mods the sum of the src coordinates and subsequent stroke offsets
+with the width and height of the pattern. For image cloning, if the sum of the
+src coordinates and subsequent stroke offsets exceeds the extents of the src
+drawable, then no paint is transferred. The clone tool is capable of
+transforming between any image types including RGB->Indexed--although
+converting from any type to indexed is significantly slower.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+       { name => 'src_drawable', type => 'drawable',
+         desc => 'The source drawable' },
+       { name => 'clone_type', type => 'enum GimpCloneType',
+         desc => 'The type of clone' },
+       { name => 'src_x', type => 'float',
+         desc => 'The x coordinate in the source image' },
+       { name => 'src_y', type => 'float',
+         desc => 'The y coordinate in the source image' },
+       &stroke_arg
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-clone");
+
+  if (options &&
+      gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      options = gimp_config_duplicate (GIMP_CONFIG (options));
+
+      g_object_set (options,
+                    "clone-type", clone_type,
+                    NULL);
+
+      success = paint_tools_stroke (gimp, context, options, drawable,
+                                    num_strokes, strokes, error,
+                                    "undo-desc",    options->paint_info->blurb,
+                                    "src-drawable", src_drawable,
+                                    "src-x",        (gint) floor (src_x),
+                                    "src-y",        (gint) floor (src_y),
+                                    NULL);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub clone_default {
+    $blurb = <<'BLURB';
+Clone from the source to the dest drawable using the current brush
+BLURB
+
+    $help = <<'HELP';
+This tool clones (copies) from the source drawable starting at the specified
+source coordinates to the dest drawable. This function performs exactly
+the same as the gimp_clone() function except that the tools arguments are
+obtained from the clones option dialog. It this dialog has not been activated
+then the dialogs default values will be used.
+HELP
+
+    &andy_pdb_misc('1999');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+       &stroke_arg
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-clone");
+
+  if (options &&
+      gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      options = gimp_config_duplicate (GIMP_CONFIG (options));
+
+      success = paint_tools_stroke (gimp, context, options, drawable,
+                                    num_strokes, strokes, error,
+                                   "undo-desc", options->paint_info->blurb,
+                                   NULL);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+sub convolve_default {
+    $blurb = 'Convolve (Blur, Sharpen) using the current brush.';
+
+    $help = <<'HELP';
+This tool convolves the specified drawable with either a sharpening or blurring
+kernel. This function performs exactly the same as the gimp_convolve()
+function except that the tools arguments are obtained from the convolve
+option dialog. It this dialog has not been activated then the dialogs
+default values will be used.
+HELP
+
+    &andy_pdb_misc('1999');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+       &stroke_arg
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-convolve");
+
+  if (options &&
+      gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      options = gimp_config_duplicate (GIMP_CONFIG (options));
+
+      success = paint_tools_stroke (gimp, context, options, drawable,
+                                    num_strokes, strokes, error,
+                                   "undo-desc", options->paint_info->blurb,
+                                   NULL);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub convolve {
+    $blurb = 'Convolve (Blur, Sharpen) using the current brush.';
+
+    $help = <<'HELP';
+This tool convolves the specified drawable with either a sharpening or blurring
+kernel. The pressure parameter controls the magnitude of the operation. Like
+the paintbrush, this tool linearly interpolates between the specified stroke
+coordinates.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+       { name => 'pressure', type => '0 <= float <= 100',
+         desc => 'The pressure' },
+       { name => 'convolve_type', type => 'enum GimpConvolveType',
+         desc => 'Convolve type' },
+       &stroke_arg
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-convolve");
+
+  if (options &&
+      gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      options = gimp_config_duplicate (GIMP_CONFIG (options));
+
+      g_object_set (options,
+                    "type", convolve_type,
+                    "rate", pressure,
+                    NULL);
+
+      success = paint_tools_stroke (gimp, context, options, drawable,
+                                    num_strokes, strokes, error,
+                                   "undo-desc", options->paint_info->blurb,
+                                   NULL);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub eraser_default {
+    $blurb = 'Erase using the current brush.';
+
+    $help = <<'HELP';
+This tool erases using the current brush mask. This function performs exactly
+the same as the gimp_eraser() function except that the tools arguments are
+obtained from the eraser option dialog. It this dialog has not been activated
+then the dialogs default values will be used.
+HELP
+
+    &andy_pdb_misc('1999');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+       &stroke_arg
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-eraser");
+
+  if (options &&
+      gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      options = gimp_config_duplicate (GIMP_CONFIG (options));
+
+      success = paint_tools_stroke (gimp, context, options, drawable,
+                                    num_strokes, strokes, error,
+                                   "undo-desc", options->paint_info->blurb,
+                                   NULL);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub eraser {
+    $blurb = 'Erase using the current brush.';
+
+    $help = <<'HELP';
+This tool erases using the current brush mask. If the specified drawable
+contains an alpha channel, then the erased pixels will become transparent.
+Otherwise, the eraser tool replaces the contents of the drawable with the
+background color. Like paintbrush, this tool linearly interpolates between the
+specified stroke coordinates.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+       &stroke_arg,
+       { name => 'hardness', type => 'enum GimpBrushApplicationMode',
+         desc => 'How to apply the brush' },
+       { name => 'method', type => 'enum GimpPaintApplicationMode',
+         desc => 'The paint method to use' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-eraser");
+
+  if (options &&
+      gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      options = gimp_config_duplicate (GIMP_CONFIG (options));
+
+      g_object_set (options,
+                    "application-mode", method,
+                    "hard",             hardness,
+                    NULL);
+
+      success = paint_tools_stroke (gimp, context, options, drawable,
+                                    num_strokes, strokes, error,
+                                   "undo-desc", options->paint_info->blurb,
+                                   NULL);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub anti_eraser {
+    $blurb = 'Anti-erase using the current brush.';
+
+    $help = <<'HELP';
+This tool anti-erases using the current brush mask. If the specified drawable
+contains an alpha channel, then the erased pixels will become opaque.
+Otherwise, the eraser tool replaces the contents of the drawable with the
+background color. Like paintbrush, this tool linearly interpolates between the
+specified stroke coordinates.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+       &stroke_arg,
+       { name => 'hardness', type => 'enum GimpBrushApplicationMode',
+         desc => 'How to apply the brush' },
+       { name => 'method', type => 'enum GimpPaintApplicationMode',
+         desc => 'The paint method to use' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-eraser");
+
+  if (options &&
+      gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      options = gimp_config_duplicate (GIMP_CONFIG (options));
+
+      g_object_set (options,
+                    "application-mode", method,
+                    "hard",             hardness,
+                    "anti-erase",       TRUE,
+                    NULL);
+
+      success = paint_tools_stroke (gimp, context, options, drawable,
+                                    num_strokes, strokes, error,
+                                   "undo-desc", options->paint_info->blurb,
+                                   NULL);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub heal {
+    $blurb = <<'BLURB';
+Heal from the source to the dest drawable using the current brush
+BLURB
+
+    $help = <<'HELP';
+This tool heals the source drawable starting at the specified
+source coordinates to the dest drawable. For image healing, if the sum of the
+src coordinates and subsequent stroke offsets exceeds the extents of the src
+drawable, then no paint is transferred. The healing tool is capable of
+transforming between any image types except RGB->Indexed.
+HELP
+
+    &kevins_pdb_misc('2006', '2.4');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+       { name => 'src_drawable', type => 'drawable',
+         desc => 'The source drawable' },
+       { name => 'src_x', type => 'float',
+         desc => 'The x coordinate in the source image' },
+       { name => 'src_y', type => 'float',
+         desc => 'The y coordinate in the source image' },
+       &stroke_arg
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-heal");
+
+  if (options &&
+      gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      options = gimp_config_duplicate (GIMP_CONFIG (options));
+
+      success = paint_tools_stroke (gimp, context, options, drawable,
+                                    num_strokes, strokes, error,
+                                   "undo-desc",    options->paint_info->blurb,
+                                    "src-drawable", src_drawable,
+                                    "src-x",        (gint) floor (src_x),
+                                    "src-y",        (gint) floor (src_y),
+                                    NULL);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub heal_default {
+    $blurb = <<'BLURB';
+Heal from the source to the dest drawable using the current brush
+BLURB
+
+    $help = <<'HELP';
+This tool heals from the source drawable starting at the specified
+source coordinates to the dest drawable. This function performs exactly
+the same as the gimp_heal() function except that the tools arguments are
+obtained from the healing option dialog. It this dialog has not been activated
+then the dialogs default values will be used.
+HELP
+
+    &kevins_pdb_misc('2006', '2.4');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+       &stroke_arg
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-heal");
+
+  if (options &&
+      gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      options = gimp_config_duplicate (GIMP_CONFIG (options));
+
+      success = paint_tools_stroke (gimp, context, options, drawable,
+                                    num_strokes, strokes, error,
+                                   "undo-desc", options->paint_info->blurb,
+                                   NULL);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub paintbrush {
+    $blurb = <<'BLURB';
+Paint in the current brush with optional fade out parameter and pull colors
+from a gradient.
+BLURB
+
+    $help = <<'HELP';
+This tool is the standard paintbrush. It draws linearly interpolated lines
+through the specified stroke coordinates. It operates on the specified drawable
+in the foreground color with the active brush. The 'fade-out' parameter is
+measured in pixels and allows the brush stroke to linearly fall off. The
+pressure is set to the maximum at the beginning of the stroke. As the distance
+of the stroke nears the fade-out value, the pressure will approach zero. The
+gradient-length is the distance to spread the gradient over. It is measured in
+pixels. If the gradient-length is 0, no gradient is used.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+       { name => 'fade_out', type => '0 <= float',
+         desc => 'Fade out parameter' },
+         &stroke_arg,
+       { name => 'method', type => 'enum GimpPaintApplicationMode',
+         desc => 'The paint method to use' },
+       { name => 'gradient_length', type => '0 <= float',
+         desc => 'Length of gradient to draw' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-paintbrush");
+
+  if (options &&
+      gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GimpDynamics *pdb_dynamics  = GIMP_DYNAMICS (gimp_dynamics_new (context, "pdb"));
+      GimpDynamics *user_dynamics = gimp_context_get_dynamics (context);
+
+      options = gimp_config_duplicate (GIMP_CONFIG (options));
+
+      g_object_set (options,
+                    "application-mode", method,
+                    "fade-length",      MAX (fade_out, gradient_length),
+                    NULL);
+
+      if (fade_out > 0)
+        {
+           GimpDynamicsOutput *opacity_output =
+             gimp_dynamics_get_output (pdb_dynamics,
+                                       GIMP_DYNAMICS_OUTPUT_OPACITY);
+
+           g_object_set (opacity_output,
+                         "use-fade", TRUE,
+                         NULL);
+        }
+
+      if (gradient_length > 0)
+        {
+          GimpDynamicsOutput *color_output =
+            gimp_dynamics_get_output (pdb_dynamics,
+                                      GIMP_DYNAMICS_OUTPUT_COLOR);
+
+          g_object_set (color_output,
+                        "use-fade", TRUE,
+                        NULL);
+        }
+
+      gimp_context_set_dynamics (context, pdb_dynamics);
+
+      success = paint_tools_stroke (gimp, context, options, drawable,
+                                    num_strokes, strokes, error,
+                                   "undo-desc", options->paint_info->blurb,
+                                   NULL);
+
+      gimp_context_set_dynamics (context, user_dynamics);
+
+      g_object_unref (pdb_dynamics);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub paintbrush_default {
+    $blurb = <<'BLURB';
+Paint in the current brush. The fade out parameter and pull colors
+from a gradient parameter are set from the paintbrush options dialog. If this
+dialog has not been activated then the dialog defaults will be used.
+BLURB
+
+    $help = <<'HELP';
+This tool is similar to the standard paintbrush. It draws linearly interpolated lines
+through the specified stroke coordinates. It operates on the specified drawable
+in the foreground color with the active brush. The 'fade-out' parameter is
+measured in pixels and allows the brush stroke to linearly fall
+off (value obtained from the option dialog). The pressure is set
+to the maximum at the beginning of the stroke. As the distance
+of the stroke nears the fade-out value, the pressure will approach zero. The
+gradient-length (value obtained from the option dialog) is the
+distance to spread the gradient over. It is measured in pixels. If
+the gradient-length is 0, no gradient is used.
+HELP
+
+    &andy_pdb_misc('1999');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+       &stroke_arg
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-paintbrush");
+
+  if (options &&
+      gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      options = gimp_config_duplicate (GIMP_CONFIG (options));
+
+      success = paint_tools_stroke (gimp, context, options, drawable,
+                                    num_strokes, strokes, error,
+                                   "undo-desc", options->paint_info->blurb,
+                                   NULL);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub pencil {
+    $blurb = 'Paint in the current brush without sub-pixel sampling.';
+
+    $help = <<'HELP';
+This tool is the standard pencil. It draws linearly interpolated lines through
+the specified stroke coordinates. It operates on the specified drawable in the
+foreground color with the active brush. The brush mask is treated as though it
+contains only black and white values. Any value below half is treated as black;
+any above half, as white.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+       &stroke_arg
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-pencil");
+
+  if (options &&
+      gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      options = gimp_config_duplicate (GIMP_CONFIG (options));
+
+      success = paint_tools_stroke (gimp, context, options, drawable,
+                                    num_strokes, strokes, error,
+                                   "undo-desc", options->paint_info->blurb,
+                                   NULL);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub smudge {
+    $blurb  = 'Smudge image with varying pressure.';
+
+    $help = <<'HELP';
+This tool simulates a smudge using the current brush. High pressure results
+in a greater smudge of paint while low pressure results in a lesser smudge.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+        { name => 'pressure', type => '0 <= float <= 100',
+          desc => 'The pressure of the smudge strokes' },
+          &stroke_arg
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-smudge");
+
+  if (options &&
+      gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      options = gimp_config_duplicate (GIMP_CONFIG (options));
+
+      g_object_set (options,
+                    "rate", pressure,
+                    NULL);
+
+      success = paint_tools_stroke (gimp, context, options, drawable,
+                                    num_strokes, strokes, error,
+                                   "undo-desc", options->paint_info->blurb,
+                                   NULL);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub smudge_default {
+    $blurb  = <<'BLURB';
+Smudge image with varying pressure.
+BLURB
+
+    $help = <<'HELP';
+This tool simulates a smudge using the current brush. It behaves exactly
+the same as gimp_smudge() except that the pressure value is taken from the
+smudge tool options or the options default if the tools option dialog has
+not been activated.
+HELP
+
+    &andy_pdb_misc('1999');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+       &stroke_arg
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-smudge");
+
+  if (options &&
+      gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      options = gimp_config_duplicate (GIMP_CONFIG (options));
+
+      success = paint_tools_stroke (gimp, context, options, drawable,
+                                    num_strokes, strokes, error,
+                                   "undo-desc", options->paint_info->blurb,
+                                   NULL);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub dodgeburn {
+    $blurb  = <<'BLURB';
+Dodgeburn image with varying exposure.
+BLURB
+
+    $help = <<'HELP';
+Dodgeburn. More details here later.
+HELP
+
+    &andy_pdb_misc('1999');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+        { name => 'exposure', type => '0 <= float <= 100',
+          desc => 'The exposure of the  strokes' },
+        { name => 'dodgeburn_type', type => 'enum GimpDodgeBurnType',
+         desc => 'The type either dodge or burn' },
+        { name => 'dodgeburn_mode', type => 'enum GimpTransferMode',
+         desc => 'The mode' },
+       &stroke_arg
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-dodge-burn");
+
+  if (options &&
+      gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      options = gimp_config_duplicate (GIMP_CONFIG (options));
+
+      g_object_set (options,
+                    "type",     dodgeburn_type,
+                    "mode",     dodgeburn_mode,
+                    "exposure", exposure,
+                    NULL);
+
+      success = paint_tools_stroke (gimp, context, options, drawable,
+                                    num_strokes, strokes, error,
+                                   "undo-desc", options->paint_info->blurb,
+                                   NULL);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub dodgeburn_default {
+    $blurb  = <<'BLURB';
+Dodgeburn image with varying exposure. This is the same as the
+gimp_dodgeburn() function except that the exposure, type and mode are
+taken from the tools option dialog. If the dialog has not been
+activated then the defaults as used by the dialog will be used.
+BLURB
+
+    $help = <<'HELP';
+Dodgeburn. More details here later.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+       &stroke_arg
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPaintOptions *options =
+    gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context),
+                                        "gimp-dodge-burn");
+
+  if (options &&
+      gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      options = gimp_config_duplicate (GIMP_CONFIG (options));
+
+      success = paint_tools_stroke (gimp, context, options, drawable,
+                                    num_strokes, strokes, error,
+                                   "undo-desc", options->paint_info->blurb,
+                                   NULL);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+$extra{app}->{code} = <<'CODE';
+static const GimpCoords default_coords = GIMP_COORDS_DEFAULT_VALUES;
+
+static gboolean
+paint_tools_stroke (Gimp              *gimp,
+                    GimpContext       *context,
+                    GimpPaintOptions  *options,
+                    GimpDrawable      *drawable,
+                    gint               n_strokes,
+                    const gdouble     *strokes,
+                    GError           **error,
+                    const gchar       *first_property_name,
+                    ...)
+{
+  GimpPaintCore *core;
+  GimpCoords    *coords;
+  gboolean       retval;
+  gint           i;
+  va_list        args;
+
+  n_strokes /= 2;  /* #doubles -> #points */
+
+  /*  undefine the paint-relevant context properties and get them
+   *  from the current context
+   */
+  gimp_context_define_properties (GIMP_CONTEXT (options),
+                                  GIMP_CONTEXT_PROP_MASK_PAINT,
+                                  FALSE);
+  gimp_context_set_parent (GIMP_CONTEXT (options), context);
+
+  va_start (args, first_property_name);
+  core = GIMP_PAINT_CORE (g_object_new_valist (options->paint_info->paint_type,
+                                               first_property_name, args));
+  va_end (args);
+
+  coords = g_new (GimpCoords, n_strokes);
+
+  for (i = 0; i < n_strokes; i++)
+    {
+      coords[i]   = default_coords;
+      coords[i].x = strokes[2 * i];
+      coords[i].y = strokes[2 * i + 1];
+    }
+
+  retval = gimp_paint_core_stroke (core, drawable, options,
+                                   coords, n_strokes, TRUE,
+                                   error);
+
+  g_free (coords);
+
+  g_object_unref (core);
+  g_object_unref (options);
+
+  return retval;
+}
+CODE
+
+
+@headers = qw("libgimpmath/gimpmath.h"
+              "libgimpconfig/gimpconfig.h"
+              "core/gimpdynamics.h"
+              "core/gimppaintinfo.h"
+              "paint/gimppaintcore.h"
+              "paint/gimppaintcore-stroke.h"
+              "paint/gimppaintoptions.h"
+              "gimppdbcontext.h"
+              "gimppdb-utils.h");
+
+@procs = qw(airbrush airbrush_default
+            clone clone_default
+            convolve convolve_default
+            dodgeburn dodgeburn_default
+           eraser eraser_default
+            heal heal_default
+            paintbrush paintbrush_default
+           pencil
+            smudge smudge_default);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Paint Tool procedures';
+$doc_title = 'gimppainttools';
+$doc_short_desc = 'Access to toolbox paint tools.';
+$doc_long_desc = 'Functions giving access to toolbox paint tools.';
+
+1;
diff --git a/pdb/groups/palette.pdb b/pdb/groups/palette.pdb
new file mode 100644
index 0000000..da5dc4c
--- /dev/null
+++ b/pdb/groups/palette.pdb
@@ -0,0 +1,587 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub palette_new {
+    $blurb = "Creates a new palette";
+    $help  = "This procedure creates a new, uninitialized palette";
+
+    &mitch_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The requested name of the new palette' }
+    );
+
+    @outargs = (
+       { name => 'actual_name', type => 'string',
+         desc => 'The actual new palette name' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpData *data = gimp_data_factory_data_new (gimp->palette_factory,
+                                               context, name);
+
+  if (data)
+    actual_name = g_strdup (gimp_object_get_name (data));
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub palette_is_editable {
+    $blurb = "Tests if palette can be edited";
+    $help  = "Returns TRUE if you have permission to change the palette";
+
+    &bill_pdb_misc('2004', '2.4');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The palette name' }
+    );
+
+    @outargs = (
+       { name => 'editable', type => 'boolean',
+         desc => "TRUE if the palette can be edited" }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpPalette *palette = gimp_pdb_get_palette (gimp, name, GIMP_PDB_DATA_ACCESS_READ, error);
+
+  if (palette)
+    editable = gimp_data_is_writable (GIMP_DATA (palette));
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub palette_duplicate {
+    $blurb = "Duplicates a palette";
+    $help  = "This procedure creates an identical palette by a different name";
+
+    &mitch_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The palette name' }
+    );
+
+    @outargs = (
+       { name => 'copy_name', type => 'string', non_empty => 1,
+         desc => "The name of the palette's copy" }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpPalette *palette = gimp_pdb_get_palette (gimp, name, GIMP_PDB_DATA_ACCESS_READ, error);
+
+  if (palette)
+    {
+      GimpPalette *palette_copy = (GimpPalette *)
+        gimp_data_factory_data_duplicate (gimp->palette_factory,
+                                          GIMP_DATA (palette));
+
+      if (palette_copy)
+        copy_name = g_strdup (gimp_object_get_name (palette_copy));
+      else
+        success = FALSE;
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub palette_rename {
+    $blurb = "Rename a palette";
+    $help  = "This procedure renames a palette";
+
+    &mitch_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The palette name' },
+        { name => 'new_name', type => 'string', non_empty => 1,
+          desc => "The new name of the palette" }
+    );
+
+    @outargs = (
+       { name => 'actual_name', type => 'string',
+         desc => "The actual new name of the palette" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPalette *palette = gimp_pdb_get_palette (gimp, name, GIMP_PDB_DATA_ACCESS_RENAME, error);
+
+  if (palette)
+    {
+      gimp_object_set_name (GIMP_OBJECT (palette), new_name);
+      actual_name = g_strdup (gimp_object_get_name (palette));
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub palette_delete {
+    $blurb = "Deletes a palette";
+    $help  = "This procedure deletes a palette";
+
+    &mitch_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The palette name' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPalette *palette = gimp_pdb_get_palette (gimp, name, GIMP_PDB_DATA_ACCESS_READ, error);
+
+  if (palette && gimp_data_is_deletable (GIMP_DATA (palette)))
+    success = gimp_data_factory_data_delete (gimp->palette_factory,
+                                             GIMP_DATA (palette),
+                                             TRUE, error);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub palette_get_info {
+    $blurb = 'Retrieve information about the specified palette.';
+
+    $help = <<'HELP';
+This procedure retrieves information about the specified palette. This
+includes the name, and the number of colors.
+HELP
+
+    &mitch_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The palette name' }
+    );
+
+    @outargs = (
+        { name => 'num_colors', type => 'int32', void_ret => 1,
+          desc => 'The number of colors in the palette' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPalette *palette = gimp_pdb_get_palette (gimp, name, GIMP_PDB_DATA_ACCESS_READ, error);
+
+  if (palette)
+    num_colors = gimp_palette_get_n_colors (palette);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub palette_get_colors {
+    $blurb = 'Gets all colors from the specified palette.';
+
+    $help = <<'HELP';
+This procedure retrieves all color entries of the specified palette. 
+HELP
+
+    &neo_pdb_misc('2006', '2.6');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The palette name' }
+    );
+
+    @outargs = (
+       { name => 'colors', type => 'colorarray',
+         desc => 'The colors in the palette',
+         array => { name => 'num_colors',
+                     desc => 'Length of the colors array' } }
+    );
+
+    %invoke = (
+       vars => [ 'GimpPalette *palette = NULL' ],
+       code => <<'CODE'
+{
+  GimpPalette *palette = gimp_pdb_get_palette (gimp, name, GIMP_PDB_DATA_ACCESS_READ, error);
+
+  if (palette)
+    {
+      GList *list = gimp_palette_get_colors (palette);
+      gint   i;
+
+      num_colors = gimp_palette_get_n_colors (palette);
+      colors     = g_new (GimpRGB, num_colors);
+
+      for (i = 0; i < num_colors; i++, list = g_list_next (list))
+        {
+          GimpPaletteEntry *entry = list->data;
+
+          colors[i] = entry->color;
+        }
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub palette_get_columns {
+    $blurb = "Retrieves the number of columns to use to display this palette";
+    $help = <<'HELP';
+This procedures retrieves the preferred number of columns to use when the
+palette is being displayed.
+HELP
+
+    &neo_pdb_misc('2005', '2.4');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The palette name' }
+    );
+
+    @outargs = (
+        { name => 'num_columns', type => 'int32',
+          desc => "The number of columns used to display this palette" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPalette *palette = gimp_pdb_get_palette (gimp, name, GIMP_PDB_DATA_ACCESS_READ, error);
+
+  if (palette)
+    num_columns = gimp_palette_get_columns (palette);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub palette_set_columns {
+    $blurb = "Sets the number of columns to use when displaying the palette";
+    $help = <<'HELP';
+This procedures controls how many colors are shown per row when the
+palette is being displayed. This value can only be changed if the
+palette is writable. The maximum allowed value is 64.
+HELP
+
+    &neo_pdb_misc('2005', '2.4');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The palette name' },
+        { name => 'columns', type => '0 <= int32 <= 64',
+          desc => "The new number of columns" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPalette *palette = gimp_pdb_get_palette (gimp, name, GIMP_PDB_DATA_ACCESS_WRITE, error);
+
+  if (palette)
+    gimp_palette_set_columns (palette, columns);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub palette_add_entry {
+    $blurb = 'Adds a palette entry to the specified palette.';
+
+    $help = <<'HELP';
+This procedure adds an entry to the specified palette.
+It returns an error if the entry palette does not exist.
+HELP
+
+    &mitch_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The palette name' },
+        { name => 'entry_name', type => 'string', null_ok => 1,
+          desc => 'The name of the entry' },
+        { name => 'color', type => 'color',
+          desc => 'The new entry\'s color color' }
+    );
+
+    @outargs = (
+        { name => 'entry_num', type => 'int32', void_ret => 1,
+          desc => 'The index of the added entry' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPalette *palette = gimp_pdb_get_palette (gimp, name, GIMP_PDB_DATA_ACCESS_WRITE, error);
+
+  if (palette)
+    {
+      GimpPaletteEntry *entry =
+        gimp_palette_add_entry (palette, -1, entry_name, &color);
+
+      entry_num = entry->position;
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub palette_delete_entry {
+    $blurb = 'Deletes a palette entry from the specified palette.';
+
+    $help = <<'HELP';
+This procedure deletes an entry from the specified palette.
+It returns an error if the entry palette does not exist.
+HELP
+
+    &mitch_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The palette name' },
+        { name => 'entry_num', type => 'int32',
+          desc => 'The index of the added entry' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPalette *palette = gimp_pdb_get_palette (gimp, name, GIMP_PDB_DATA_ACCESS_WRITE, error);
+
+  if (palette)
+    {
+      GimpPaletteEntry *entry = gimp_palette_get_entry (palette, entry_num);
+
+      if (entry)
+        gimp_palette_delete_entry (palette, entry);
+      else
+        success = FALSE;
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub palette_entry_get_color {
+    $blurb = 'Gets the specified palette entry from the specified palette.';
+
+    $help = <<'HELP';
+This procedure retrieves the color of the zero-based entry specified for the
+specified palette. It returns an error if the entry does not exist.
+HELP
+
+    &mitch_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The palette name' },
+        { name => 'entry_num', type => 'int32',
+          desc => 'The entry to retrieve' }
+    );
+
+    @outargs = (
+        { name => 'color', type => 'color', void_ret => 1,
+          desc => 'The color requested' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPalette *palette = gimp_pdb_get_palette (gimp, name, GIMP_PDB_DATA_ACCESS_READ, error);
+
+  if (palette)
+    {
+      GimpPaletteEntry *entry = gimp_palette_get_entry (palette, entry_num);
+
+      if (entry)
+        color = entry->color;
+      else
+        success = FALSE;
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub palette_entry_set_color {
+    $blurb = 'Sets the specified palette entry in the specified palette.';
+
+    $help = <<'HELP';
+This procedure sets the color of the zero-based entry specified for the
+specified palette. It returns an error if the entry does not exist.
+HELP
+
+    &mitch_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The palette name' },
+        { name => 'entry_num', type => 'int32',
+          desc => 'The entry to retrieve' },
+        { name => 'color', type => 'color',
+          desc => 'The new color' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPalette *palette = gimp_pdb_get_palette (gimp, name, GIMP_PDB_DATA_ACCESS_WRITE, error);
+
+  if (palette)
+    success = gimp_palette_set_entry_color (palette, entry_num, &color);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub palette_entry_get_name {
+    $blurb = 'Gets the specified palette entry from the specified palette.';
+
+    $help = <<'HELP';
+This procedure retrieves the name of the zero-based entry specified for the
+specified palette. It returns an error if the entry does not exist.
+HELP
+
+    &mitch_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The palette name' },
+        { name => 'entry_num', type => 'int32',
+          desc => 'The entry to retrieve' }
+    );
+
+    @outargs = (
+        { name => 'entry_name', type => 'string', void_ret => 1,
+          desc => 'The name requested' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPalette *palette = gimp_pdb_get_palette (gimp, name, GIMP_PDB_DATA_ACCESS_READ, error);
+
+  if (palette)
+    {
+      GimpPaletteEntry *entry = gimp_palette_get_entry (palette, entry_num);
+
+      if (entry)
+        entry_name = g_strdup (entry->name);
+      else
+        success = FALSE;
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub palette_entry_set_name {
+    $blurb = 'Sets the specified palette entry in the specified palette.';
+
+    $help = <<'HELP';
+This procedure sets the name of the zero-based entry specified for the
+specified palette. It returns an error if the entry does not exist.
+HELP
+
+    &mitch_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The palette name' },
+        { name => 'entry_num', type => 'int32',
+          desc => 'The entry to retrieve' },
+        { name => 'entry_name', type => 'string', null_ok => 1,
+          desc => 'The new name' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPalette *palette = gimp_pdb_get_palette (gimp, name, GIMP_PDB_DATA_ACCESS_WRITE, error);
+
+  if (palette)
+    success = gimp_palette_set_entry_name (palette, entry_num, entry_name);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+@headers = qw(<string.h>
+              "core/gimp.h"
+              "core/gimpcontext.h"
+              "core/gimpdatafactory.h"
+              "core/gimppalette.h"
+              "gimppdb-utils.h");
+
+@procs = qw(palette_new
+            palette_duplicate
+            palette_rename
+            palette_delete
+            palette_is_editable
+            palette_get_info palette_get_colors
+            palette_get_columns palette_set_columns
+            palette_add_entry palette_delete_entry
+            palette_entry_get_color palette_entry_set_color
+            palette_entry_get_name palette_entry_set_name);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Palette';
+$doc_title = 'gimppalette';
+$doc_short_desc = 'Functions operating on a single palette.';
+$doc_long_desc = 'Functions operating on a single palette.';
+
+1;
diff --git a/pdb/groups/palette_select.pdb b/pdb/groups/palette_select.pdb
new file mode 100644
index 0000000..5022e25
--- /dev/null
+++ b/pdb/groups/palette_select.pdb
@@ -0,0 +1,116 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub palettes_popup {
+    $blurb = 'Invokes the Gimp palette selection.';
+    $help  = 'This procedure opens the palette selection dialog.';
+
+    &mitch_pdb_misc('2002');
+
+    @inargs = (
+       { name => 'palette_callback', type => 'string', non_empty => 1,
+         desc => 'The callback PDB proc to call when palette selection is
+                  made' },
+       { name => 'popup_title', type => 'string',
+         desc => 'Title of the palette selection dialog' },
+       { name => 'initial_palette', type => 'string', null_ok => 1,
+         desc => 'The name of the palette to set as the first selected' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp->no_interface ||
+      ! gimp_pdb_lookup_procedure (gimp->pdb, palette_callback) ||
+      ! gimp_pdb_dialog_new (gimp, context, progress,
+                             gimp_data_factory_get_container (gimp->palette_factory),
+                             popup_title, palette_callback, initial_palette,
+                             NULL))
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub palettes_close_popup {
+    $blurb = 'Close the palette selection dialog.';
+    $help  = 'This procedure closes an opened palette selection dialog.';
+
+    &mitch_pdb_misc('2002');
+
+    @inargs = (
+       { name => 'palette_callback', type => 'string', non_empty => 1,
+         desc => 'The name of the callback registered for this pop-up' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp->no_interface ||
+      ! gimp_pdb_lookup_procedure (gimp->pdb, palette_callback) ||
+      ! gimp_pdb_dialog_close (gimp, gimp_data_factory_get_container (gimp->palette_factory),
+                               palette_callback))
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub palettes_set_popup {
+    $blurb = 'Sets the current palette in a palette selection dialog.';
+    $help  = $blurb;
+
+    &mitch_pdb_misc('2002');
+
+    @inargs = (
+       { name => 'palette_callback', type => 'string', non_empty => 1,
+         desc => 'The name of the callback registered for this pop-up' },
+       { name => 'palette_name', type => 'string',
+         desc => 'The name of the palette to set as selected' },
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp->no_interface ||
+      ! gimp_pdb_lookup_procedure (gimp->pdb, palette_callback) ||
+      ! gimp_pdb_dialog_set (gimp, gimp_data_factory_get_container (gimp->palette_factory),
+                             palette_callback, palette_name,
+                             NULL))
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+@headers = qw("core/gimp.h"
+              "core/gimpdatafactory.h");
+
+@procs = qw(palettes_popup
+            palettes_close_popup
+            palettes_set_popup);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Palette UI';
+$doc_title = 'gimppaletteselect';
+$doc_short_desc = 'Functions providing a palette selection dialog.';
+$doc_long_desc = 'Functions providing a palette selection dialog.';
+
+1;
diff --git a/pdb/groups/palettes.pdb b/pdb/groups/palettes.pdb
new file mode 100644
index 0000000..6ba01eb
--- /dev/null
+++ b/pdb/groups/palettes.pdb
@@ -0,0 +1,167 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub palettes_refresh {
+    $blurb = 'Refreshes current palettes. This function always succeeds.';
+
+    $help = <<'HELP';
+This procedure retrieves all palettes currently in the user's palette path
+and updates the palette dialogs accordingly.
+HELP
+
+    &adrian_pdb_misc('1998');
+
+    %invoke = (
+        code => <<'CODE'
+{
+  gimp_data_factory_data_refresh (gimp->palette_factory, context);
+}
+CODE
+    );
+}
+
+sub palettes_get_list {
+    $blurb = 'Retrieves a list of all of the available palettes';
+
+    $help = <<'HELP';
+This procedure returns a complete listing of available palettes. Each name
+returned can be used as input to the command gimp_context_set_palette().
+HELP
+
+    &rock_pdb_misc('2001');
+
+    @inargs = (
+       { name => 'filter', type => 'string', null_ok => 1,
+          desc => 'An optional regular expression used to filter the list' }
+    );
+
+    @outargs = (
+        { name => 'palette_list', type => 'stringarray',
+          desc => 'The list of palette names',
+          array => { name => 'num_palettes',
+                     desc => 'The number of palettes in the list' } }
+    );
+
+    %invoke = (
+        headers => [ qw("core/gimpcontainer-filter.h") ],
+        code    => <<'CODE'
+{
+  palette_list = gimp_container_get_filtered_name_array (gimp_data_factory_get_container 
(gimp->palette_factory),
+                                                         filter, &num_palettes);
+}
+CODE
+    );
+}
+
+sub palettes_get_palette {
+    &std_pdb_deprecated ('gimp-context-get-palette');
+
+    @outargs = (
+       { name => 'name', type => 'string', null_ok => 1,
+         desc => 'The palette name' },
+       { name => 'num_colors', type => 'int32',
+         desc => 'The palette num_colors' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpPalette *palette = gimp_context_get_palette (context);
+
+  if (palette)
+    {
+      name       = g_strdup (gimp_object_get_name (palette));
+      num_colors = gimp_palette_get_n_colors (palette);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub palettes_get_palette_entry {
+    &std_pdb_deprecated ('gimp-palette-entry-get-color');
+
+    @inargs = (
+       { name => 'name', type => 'string', null_ok => 1,
+         desc => 'The palette name ("" means currently active palette)' },
+        { name => 'entry_num', type => 'int32',
+          desc => 'The entry to retrieve' }
+    );
+
+    @outargs = (
+       { name => 'actual_name', type => 'string',
+         desc => 'The palette name' },
+       { name => 'num_colors', type => 'int32',
+         desc => 'The palette num_colors' },
+       { name => 'color', type => 'color', void_ret => 1,
+         desc => 'The color requested' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPalette *palette;
+
+  if (name && strlen (name))
+    palette = gimp_pdb_get_palette (gimp, name, FALSE, error);
+  else
+    palette = gimp_context_get_palette (context);
+
+  if (palette)
+    {
+      GimpPaletteEntry *entry = gimp_palette_get_entry (palette, entry_num);
+
+      if (entry)
+        {
+          actual_name = g_strdup (gimp_object_get_name (palette));
+          num_colors  = gimp_palette_get_n_colors (palette);
+          color       = entry->color;
+        }
+      else
+        success = FALSE;
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+@headers = qw(<string.h>
+              "core/gimp.h"
+              "core/gimpcontext.h"
+              "core/gimpdatafactory.h"
+              "core/gimppalette.h"
+              "gimppdb-utils.h");
+
+@procs = qw(palettes_refresh
+            palettes_get_list
+            palettes_get_palette
+            palettes_get_palette_entry);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Palettes';
+$doc_title = 'gimppalettes';
+$doc_short_desc = 'Operations related to palettes.';
+$doc_long_desc = 'Operations related to palettes.';
+
+1;
diff --git a/pdb/groups/paths.pdb b/pdb/groups/paths.pdb
new file mode 100644
index 0000000..0bbd599
--- /dev/null
+++ b/pdb/groups/paths.pdb
@@ -0,0 +1,636 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Andy Thomas <alt gimp org>
+
+sub path_list {
+    &std_pdb_deprecated('gimp-image-get-vectors');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image to list the paths from' }
+    );
+
+    @outargs = (
+       { name => 'path_list', type => 'stringarray',
+         desc => 'List of the paths belonging to this image',
+         array => { name => 'num_paths',
+                    desc => 'The number of paths returned.' } }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  path_list = gimp_container_get_name_array (gimp_image_get_vectors (image),
+                                             &num_paths);
+}
+CODE
+    );
+}
+
+sub path_get_points {
+    &std_pdb_deprecated('gimp-vectors-stroke-get-points');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image to list the paths from' },
+       { name => 'name', type => 'string',
+         desc => 'The name of the path whose points should be listed.' }
+    );
+
+    @outargs = (
+       { name => 'path_type', type => 'int32',
+         desc => 'The type of the path. Currently only one type (1 = Bezier)
+                  is supported' },
+       { name => 'path_closed', type => 'int32',
+         desc => 'Return if the path is closed. (0 = path open, 1 = path
+                  closed)' },
+       { name => 'points_pairs', type => 'floatarray',
+         desc => 'The points in the path represented as 3 floats. The first is
+                  the x pos, next is the y pos, last is the type of the pnt.
+                  The type field is dependent on the path type. For beziers
+                  (type 1 paths) the type can either be (1.0 = BEZIER_ANCHOR,
+                  2.0 = BEZIER_CONTROL, 3.0 = BEZIER_MOVE). Note all points
+                  are returned in pixel resolution.',
+         array => { name => 'num_path_point_details',
+                    desc => 'The number of points returned. Each point is
+                             made up of (x, y, pnt_type) of floats.' } }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpVectors *vectors = gimp_image_get_vectors_by_name (image, name);
+
+  if (vectors)
+    {
+      GimpVectorsCompatPoint *points;
+      gint num_points;
+
+      path_type = 1; /* BEZIER (1.2 compat) */
+
+      points = gimp_vectors_compat_get_points (vectors, &num_points,
+                                               &path_closed);
+
+      num_path_point_details = num_points * 3;
+
+      if (points)
+        {
+          gdouble *curr_point;
+          gint     i;
+
+          points_pairs = g_new0 (gdouble, num_path_point_details);
+
+          for (i = 0, curr_point = points_pairs;
+               i < num_points;
+               i++, curr_point += 3)
+            {
+              curr_point[0] = points[i].x;
+              curr_point[1] = points[i].y;
+              curr_point[2] = points[i].type;
+            }
+
+          g_free (points);
+        }
+      else
+        success = FALSE;
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub path_get_current {
+    &std_pdb_deprecated('gimp-image-get-active-vectors');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image to get the current path from' },
+    );
+
+    @outargs = (
+       { name => 'name', type => 'string',
+         desc => 'The name of the current path.' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpVectors *vectors = gimp_image_get_active_vectors (image);
+
+  if (vectors)
+    name = g_strdup (gimp_object_get_name (vectors));
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub path_set_current {
+    &std_pdb_deprecated('gimp-image-set-active-vectors');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image in which a path will become current' },
+       { name => 'name', type => 'string',
+         desc => 'The name of the path to make current.' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpVectors *vectors = gimp_image_get_vectors_by_name (image, name);
+
+  if (vectors)
+    gimp_image_set_active_vectors (image, vectors);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub path_set_points {
+    &std_pdb_deprecated('gimp-vectors-stroke-new-from-points');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image to set the paths in' },
+       { name => 'name', type => 'string',
+         desc => 'The name of the path to create. If it exists then a unique
+                  name will be created - query the list of paths if you want
+                  to make sure that the name of the path you create is 
+                  unique. This will be set as the current path.' },
+       { name => 'ptype', type => 'int32', dead => 1,
+         desc => 'The type of the path. Currently only one type (1 = Bezier)
+                  is supported.' },
+       { name => 'points_pairs', type => 'floatarray',
+         desc => 'The points in the path represented as 3 floats. The first is
+                  the x pos, next is the y pos, last is the type of the pnt.
+                  The type field is dependent on the path type. For beziers
+                  (type 1 paths) the type can either be (1.0 = BEZIER_ANCHOR,
+                  2.0 = BEZIER_CONTROL, 3.0= BEZIER_MOVE). Note all points are
+                  returned in pixel resolution.',
+         array => { name => 'num_path_points',
+                    desc => 'The number of elements in the array, i.e. the number
+                             of points in the path * 3. Each point is
+                             made up of (x, y, type) of floats. Currently only
+                             the creation of bezier curves is allowed. The type
+                             parameter must be set to (1) to indicate a BEZIER
+                             type curve. Note that for BEZIER curves, points
+                             must be given in the following order: ACCACCAC...
+                             If the path is not closed the last control
+                             point is missed off. Points consist of three
+                             control points (control/anchor/control) so for a
+                             curve that is not closed there must be at least
+                             two points passed (2 x,y pairs). If
+                             (num_path_points/3) % 3 = 0 then the path is
+                             assumed to be closed and the points are
+                             ACCACCACCACC.' } }
+    );
+
+    %invoke = (
+       code  => <<'CODE'
+{
+  gboolean closed = FALSE;
+
+  if ((num_path_points / 3) % 3 == 0)
+    closed = TRUE;
+  else if ((num_path_points / 3) % 3 != 2)
+    success = FALSE;
+
+  if (success)
+    {
+      GimpVectors            *vectors;
+      const gdouble          *curr_point_pair;
+      GimpVectorsCompatPoint *points;
+      gint                    n_points;
+      gint                    i;
+
+      n_points = num_path_points / 3;
+
+      points = g_new0 (GimpVectorsCompatPoint, n_points);
+
+      for (i = 0, curr_point_pair = points_pairs;
+           i < n_points;
+           i++, curr_point_pair += 3)
+        {
+          points[i].x    = curr_point_pair[0];
+          points[i].y    = curr_point_pair[1];
+          points[i].type = curr_point_pair[2];
+        }
+
+      vectors = gimp_vectors_compat_new (image, name, points, n_points,
+                                         closed);
+
+      g_free (points);
+
+      if (vectors)
+        success = gimp_image_add_vectors (image, vectors, NULL, 0, TRUE);
+      else
+        success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub path_stroke_current {
+    &std_pdb_deprecated('gimp-edit-stroke-vectors');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image which contains the path to stroke' },
+    );
+
+    %invoke = (
+       headers => [ qw("core/gimpstrokeoptions.h") ],
+       code    => <<'CODE'
+{
+  GimpVectors  *vectors  = gimp_image_get_active_vectors (image);
+  GimpDrawable *drawable = gimp_image_get_active_drawable (image);
+
+  if (vectors && drawable &&
+      gimp_pdb_item_is_modifyable (GIMP_ITEM (drawable),
+                                   GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GimpStrokeOptions *options;
+      GimpPaintOptions  *paint_options;
+
+      options = gimp_stroke_options_new (gimp, context, TRUE);
+      g_object_set (options,
+                    "method", GIMP_STROKE_PAINT_METHOD,
+                    NULL);
+
+      paint_options =
+        gimp_pdb_context_get_paint_options (GIMP_PDB_CONTEXT (context), NULL);
+      paint_options = gimp_config_duplicate (GIMP_CONFIG (paint_options));
+
+      success = gimp_item_stroke (GIMP_ITEM (vectors),
+                                  drawable, context, options, paint_options,
+                                  TRUE, progress, error);
+
+      g_object_unref (options);
+      g_object_unref (paint_options);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub path_get_point_at_dist {
+    &std_pdb_deprecated('gimp-vectors-stroke-get-point-at-dist');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image the paths belongs to' },
+       { name => 'distance', type => 'float',
+         desc => 'The distance along the path.' }
+    );
+
+    @outargs = (
+       { name => 'x_point', type => 'int32',
+         desc => 'The x position of the point.' },
+       { name => 'y_point', type => 'int32',
+         desc => 'The y position of the point.' },
+       { name => 'slope', type => 'float',
+         desc => 'The slope (dy / dx) at the specified point.' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpVectors *vectors;
+  GimpStroke  *stroke;
+  gdouble      distance_along;
+  gdouble      stroke_length;
+  gdouble      stroke_distance;
+  GimpCoords   position;
+
+  vectors = gimp_image_get_active_vectors (image);
+
+  if (vectors)
+    {
+      distance_along = 0.0;
+      stroke = gimp_vectors_stroke_get_next (vectors, NULL);
+
+      while (stroke != NULL )
+        {
+          stroke_length = gimp_stroke_get_length (stroke, 0.5);
+
+          if (distance_along + stroke_length < distance)
+            {
+              distance_along += stroke_length;
+            }
+          else
+            {
+              stroke_distance = distance - distance_along;
+              stroke_distance = stroke_distance < 0 ? 0: stroke_distance;
+
+              if (!gimp_stroke_get_point_at_dist (stroke, stroke_distance, 0.5,
+                                                 &position, &slope))
+                {
+                  success = FALSE;
+                  break;
+                }
+              else
+                {
+                  success = TRUE;
+                  x_point = ROUND (position.x);
+                  y_point = ROUND (position.y);
+                  break;
+                }
+            }
+
+          stroke = gimp_vectors_stroke_get_next (vectors, stroke);
+        }
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub path_get_tattoo {
+    &std_pdb_deprecated('gimp-vectors-get-tattoo');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+       { name => 'name', type => 'string',
+         desc => 'The name of the path whose tattoo should be obtained.' }
+    );
+
+    @outargs = (
+       { name => 'tattoo', type => 'int32',
+         desc => 'The tattoo associated with the named path.' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpVectors *vectors = gimp_image_get_vectors_by_name (image, name);
+
+  if (vectors)
+    tattoo = gimp_item_get_tattoo (GIMP_ITEM (vectors));
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub path_set_tattoo {
+    &std_pdb_deprecated('gimp-vectors-set-tattoo');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+       { name => 'name', type => 'string',
+         desc => 'the name of the path whose tattoo should be set' },
+        { name => 'tattovalue', type => 'int32',
+         desc => "The tattoo associated with the name path. Only values
+                  returned from 'path_get_tattoo' should be used here" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpVectors *vectors = gimp_image_get_vectors_by_name (image, name);
+
+  if (vectors)
+    gimp_item_set_tattoo (GIMP_ITEM (vectors), tattovalue);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub get_path_by_tattoo {
+    &std_pdb_deprecated('gimp-image-get-vectors-by-tattoo');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+       { name => 'tattoo', type => 'int32',
+         desc => 'The tattoo of the required path.' }
+    );
+
+    @outargs = (
+       { name => 'name', type => 'string',
+         desc => 'The name of the path with the specified tattoo.' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpVectors *vectors = gimp_image_get_vectors_by_tattoo (image, tattoo);
+
+  if (vectors)
+    name = g_strdup (gimp_object_get_name (vectors));
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub path_delete {
+    &std_pdb_deprecated('gimp-image-remove-vectors');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image to delete the path from' },
+       { name => 'name', type => 'string',
+         desc => 'The name of the path to delete.' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpVectors *vectors = gimp_image_get_vectors_by_name (image, name);
+
+  if (vectors)
+    gimp_image_remove_vectors (image, vectors, TRUE, NULL);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub path_get_locked {
+    &std_pdb_deprecated('gimp-vectors-get-linked');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+       { name => 'name', type => 'string',
+         desc => 'The name of the path whose locked status should be
+                  obtained.' }
+    );
+
+    @outargs = (
+       { name => 'locked', type => 'boolean',
+         desc => 'TRUE if the path is locked, FALSE otherwise' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpVectors *vectors = gimp_image_get_vectors_by_name (image, name);
+
+  if (vectors)
+    locked = gimp_item_get_linked (GIMP_ITEM (vectors));
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub path_set_locked {
+    &std_pdb_deprecated('gimp-vectors-set-linked');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+       { name => 'name', type => 'string',
+         desc => 'the name of the path whose locked status should be set' },
+       { name => 'locked', type => 'boolean',
+         desc => 'Whether the path is locked' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpVectors *vectors = gimp_image_get_vectors_by_name (image, name);
+
+  if (vectors)
+    gimp_item_set_linked (GIMP_ITEM (vectors), locked, TRUE);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub path_to_selection {
+    &std_pdb_deprecated('gimp-vectors-to-selection');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+       { name => 'name', type => 'string',
+         desc => 'The name of the path which should be made into selection.' },
+        { name => 'op', type => 'enum GimpChannelOps',
+         desc => 'The desired operation with current selection' },
+       { name => 'antialias', type => 'boolean',
+         desc => 'Antialias selection.' },
+       { name => 'feather', type => 'boolean',
+         desc => 'Feather selection.' },
+       { name => 'feather_radius_x', type => 'float',
+         desc => 'Feather radius x.'  },
+       { name => 'feather_radius_y', type => 'float',
+         desc => 'Feather radius y.'  }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpVectors *vectors = gimp_image_get_vectors_by_name (image, name);
+
+  if (vectors)
+    gimp_item_to_selection (GIMP_ITEM (vectors),
+                            op,
+                            antialias,
+                            feather,
+                            feather_radius_x,
+                            feather_radius_y);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub path_import {
+    &std_pdb_deprecated('gimp-vectors-import-from-file');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+       { name => 'filename', type => 'string', allow_non_utf8 => 1,
+         desc => 'The name of the SVG file to import.' },
+       { name => 'merge', type => 'boolean',
+         desc => 'Merge paths into a single vectors object.' },
+       { name => 'scale', type => 'boolean',
+         desc => 'Scale the SVG to image dimensions.' }
+    );
+
+    %invoke = (
+        headers => [ qw("vectors/gimpvectors-import.h") ],
+       code    => <<'CODE'
+{
+  GFile *file = g_file_new_for_path (filename);
+
+  success = gimp_vectors_import_file (image, file,
+                                      merge, scale, NULL, -1, NULL, NULL);
+
+  g_object_unref (file);
+}
+CODE
+    );
+}
+
+
+@headers = qw(<string.h>
+              "libgimpconfig/gimpconfig.h"
+              "libgimpmath/gimpmath.h"
+              "core/gimplist.h"
+              "vectors/gimpanchor.h"
+              "vectors/gimpbezierstroke.h"
+              "vectors/gimpvectors.h"
+              "vectors/gimpvectors-compat.h"
+              "gimppdb-utils.h"
+              "gimppdbcontext.h"
+              "gimp-intl.h");
+
+@procs = qw(path_list path_get_current path_set_current path_delete
+           path_get_points path_set_points
+            path_stroke_current path_get_point_at_dist
+           path_get_tattoo path_set_tattoo get_path_by_tattoo
+            path_get_locked path_set_locked
+            path_to_selection path_import);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Paths';
+$doc_title = 'gimppaths';
+$doc_short_desc = 'Deprecated operations related to paths.';
+$doc_long_desc = 'Deprecated operations related to paths.';
+
+1;
diff --git a/pdb/groups/pattern.pdb b/pdb/groups/pattern.pdb
new file mode 100644
index 0000000..221d395
--- /dev/null
+++ b/pdb/groups/pattern.pdb
@@ -0,0 +1,130 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub pattern_get_info {
+    $blurb = 'Retrieve information about the specified pattern.';
+
+    $help = <<'HELP';
+This procedure retrieves information about the specified pattern.
+This includes the pattern extents (width and height).
+HELP
+
+    &mitch_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The pattern name.' }
+    );
+
+    @outargs = (
+       { name => 'width', type => 'int32', void_ret => 1,
+          desc => "The pattern width" },
+       { name => 'height', type => 'int32',
+          desc => "The pattern height" },
+       { name => 'bpp', type => 'int32',
+          desc => "The pattern bpp" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPattern *pattern = gimp_pdb_get_pattern (gimp, name, error);
+
+  if (pattern)
+    {
+      width  = gimp_temp_buf_get_width  (pattern->mask);
+      height = gimp_temp_buf_get_height (pattern->mask);
+      bpp    = babl_format_get_bytes_per_pixel (gimp_temp_buf_get_format (pattern->mask));
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub pattern_get_pixels {
+    $blurb = <<'BLURB';
+Retrieve information about the specified pattern (including pixels).
+BLURB
+
+    $help = <<'HELP';
+This procedure retrieves information about the specified. This
+includes the pattern extents (width and height), its bpp and its pixel
+data.
+HELP
+
+    &mitch_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'name', type => 'string', non_empty => 1,
+         desc => 'The pattern name.' }
+    );
+
+    @outargs = (
+       { name => 'width', type => 'int32', void_ret => 1,
+          desc => "The pattern width" },
+       { name => 'height', type => 'int32',
+          desc => "The pattern height" },
+       { name => 'bpp', type => 'int32',
+          desc => "The pattern bpp" },
+        { name => 'color_bytes', type => 'int8array',
+         desc => 'The pattern data.',
+          array => { desc => 'Number of pattern bytes' } }
+    );
+    
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPattern *pattern = gimp_pdb_get_pattern (gimp, name, error);
+
+  if (pattern)
+    {
+      width           = gimp_temp_buf_get_width  (pattern->mask);
+      height          = gimp_temp_buf_get_height (pattern->mask);
+      bpp             = babl_format_get_bytes_per_pixel (gimp_temp_buf_get_format (pattern->mask));
+      num_color_bytes = gimp_temp_buf_get_data_size (pattern->mask);
+      color_bytes     = g_memdup (gimp_temp_buf_get_data (pattern->mask),
+                                  num_color_bytes);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+@headers = qw(<string.h>
+              "core/gimpcontext.h"
+              "core/gimpdatafactory.h"
+              "core/gimppattern.h"
+              "core/gimptempbuf.h"
+              "gimppdb-utils.h");
+
+@procs = qw(pattern_get_info
+            pattern_get_pixels);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Pattern';
+$doc_title = 'gimppattern';
+$doc_short_desc = 'Functions operating on a single pattern.';
+$doc_long_desc = 'Functions operating on a single pattern.';
+
+1;
diff --git a/pdb/groups/pattern_select.pdb b/pdb/groups/pattern_select.pdb
new file mode 100644
index 0000000..deaf72d
--- /dev/null
+++ b/pdb/groups/pattern_select.pdb
@@ -0,0 +1,116 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub patterns_popup {
+    $blurb = 'Invokes the Gimp pattern selection.';
+    $help  = 'This procedure opens the pattern selection dialog.';
+
+    &andy_pdb_misc('1998');
+
+    @inargs = (
+       { name => 'pattern_callback', type => 'string', non_empty => 1,
+         desc => 'The callback PDB proc to call when pattern selection is
+                  made' },
+       { name => 'popup_title', type => 'string',
+         desc => 'Title of the pattern selection dialog' },
+       { name => 'initial_pattern', type => 'string', null_ok => 1,
+         desc => 'The name of the pattern to set as the first selected' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp->no_interface ||
+      ! gimp_pdb_lookup_procedure (gimp->pdb, pattern_callback) ||
+      ! gimp_pdb_dialog_new (gimp, context, progress,
+                             gimp_data_factory_get_container (gimp->pattern_factory),
+                             popup_title, pattern_callback, initial_pattern,
+                             NULL))
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub patterns_close_popup {
+    $blurb = 'Close the pattern selection dialog.';
+    $help  = 'This procedure closes an opened pattern selection dialog.';
+
+    &andy_pdb_misc('1998');
+
+    @inargs = (
+       { name => 'pattern_callback', type => 'string', non_empty => 1,
+         desc => 'The name of the callback registered for this pop-up' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp->no_interface ||
+      ! gimp_pdb_lookup_procedure (gimp->pdb, pattern_callback) ||
+      ! gimp_pdb_dialog_close (gimp, gimp_data_factory_get_container (gimp->pattern_factory),
+                               pattern_callback))
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub patterns_set_popup {
+    $blurb = 'Sets the current pattern in a pattern selection dialog.';
+    $help  = $blurb;
+
+    &andy_pdb_misc('1998');
+
+    @inargs = (
+       { name => 'pattern_callback', type => 'string', non_empty => 1,
+         desc => 'The name of the callback registered for this pop-up' },
+       { name => 'pattern_name', type => 'string',
+         desc => 'The name of the pattern to set as selected' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp->no_interface ||
+      ! gimp_pdb_lookup_procedure (gimp->pdb, pattern_callback) ||
+      ! gimp_pdb_dialog_set (gimp, gimp_data_factory_get_container (gimp->pattern_factory),
+                             pattern_callback, pattern_name,
+                             NULL))
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+@headers = qw("core/gimp.h"
+              "core/gimpdatafactory.h");
+
+@procs = qw(patterns_popup
+            patterns_close_popup
+            patterns_set_popup);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Pattern UI';
+$doc_title = 'gimppatternselect';
+$doc_short_desc = 'Functions providing a pattern selection dialog.';
+$doc_long_desc = 'Functions providing a pattern selection dialog.';
+
+1;
diff --git a/pdb/groups/patterns.pdb b/pdb/groups/patterns.pdb
new file mode 100644
index 0000000..747f275
--- /dev/null
+++ b/pdb/groups/patterns.pdb
@@ -0,0 +1,171 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub patterns_refresh {
+    $blurb = 'Refresh current patterns. This function always succeeds.';
+
+    $help = <<'HELP';
+This procedure retrieves all patterns currently in the user's pattern path
+and updates all pattern dialogs accordingly.
+HELP
+
+    &mitch_pdb_misc('2002');
+
+    %invoke = (
+        code => <<'CODE'
+{
+  gimp_data_factory_data_refresh (gimp->pattern_factory, context);
+}
+CODE
+    );
+}
+
+sub patterns_get_list {
+    $blurb = 'Retrieve a complete listing of the available patterns.';
+
+    $help = <<'HELP';
+This procedure returns a complete listing of available GIMP patterns. Each name
+returned can be used as input to the gimp_context_set_pattern().
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'filter', type => 'string', null_ok => 1,
+          desc => 'An optional regular expression used to filter the list' }
+    );
+
+    @outargs = (
+       { name => 'pattern_list', type => 'stringarray',
+         desc => 'The list of pattern names',
+         array => { name => 'num_patterns',
+                    desc => 'The number of patterns in the pattern list' } }
+    );
+
+    %invoke = (
+        headers => [ qw("core/gimpcontainer-filter.h") ],
+       code => <<'CODE'
+{
+  pattern_list = gimp_container_get_filtered_name_array (gimp_data_factory_get_container 
(gimp->pattern_factory),
+                                                         filter, &num_patterns);
+}
+CODE
+    );
+}
+
+sub patterns_get_pattern {
+    &std_pdb_deprecated ('gimp-context-get-pattern');
+
+    @outargs = (
+       { name => 'name', type => 'string',
+         desc => 'The pattern name' },
+       { name => 'width', type => 'int32',
+         desc => 'The pattern width' },
+       { name => 'height', type => 'int32',
+         desc => 'The pattern height' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPattern *pattern = gimp_context_get_pattern (context);
+
+  if (pattern)
+    {
+      name   = g_strdup (gimp_object_get_name (pattern));
+      width  = gimp_temp_buf_get_width  (pattern->mask);
+      height = gimp_temp_buf_get_height (pattern->mask);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub patterns_get_pattern_data {
+    &std_pdb_deprecated ('gimp-pattern-get-pixels');
+
+    @inargs = (
+       { name => 'name', type => 'string', null_ok => 1,
+         desc => 'The pattern name ("" means currently active pattern)' }
+    );
+
+    @outargs = (
+       { name => 'actual_name', type => 'string',
+         desc => 'The pattern name' },
+       { name => 'width', type => 'int32',
+         desc => 'The pattern width' },
+       { name => 'height', type => 'int32',
+         desc => 'The pattern height' },
+       { name => 'mask_bpp', type => 'int32',
+         desc => 'Pattern bytes per pixel' },
+       { name => 'mask_data', type => 'int8array',
+         desc => 'The pattern mask data',
+         array => { name => 'length',
+                    desc => 'Length of pattern mask data' } }
+    );
+    
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPattern *pattern;
+
+  if (name && strlen (name))
+    pattern = gimp_pdb_get_pattern (gimp, name, error);
+  else
+    pattern = gimp_context_get_pattern (context);
+
+  if (pattern)
+    {
+      actual_name = g_strdup (gimp_object_get_name (pattern));
+      width       = gimp_temp_buf_get_width  (pattern->mask);
+      height      = gimp_temp_buf_get_height (pattern->mask);
+      mask_bpp    = babl_format_get_bytes_per_pixel (gimp_temp_buf_get_format (pattern->mask));
+      length      = gimp_temp_buf_get_data_size (pattern->mask);
+      mask_data   = g_memdup (gimp_temp_buf_get_data (pattern->mask), length);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+@headers = qw(<string.h>
+              "core/gimp.h"
+              "core/gimpcontext.h"
+              "core/gimpdatafactory.h"
+              "core/gimppattern.h"
+              "core/gimptempbuf.h"
+              "gimppdb-utils.h");
+
+@procs = qw(patterns_refresh
+            patterns_get_list
+            patterns_get_pattern
+            patterns_get_pattern_data);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Patterns';
+$doc_title = 'gimppatterns';
+$doc_short_desc = 'Functions relating to patterns.';
+$doc_long_desc = 'Functions relating to patterns.';
+
+1;
diff --git a/pdb/groups/plug_in.pdb b/pdb/groups/plug_in.pdb
new file mode 100644
index 0000000..c024eae
--- /dev/null
+++ b/pdb/groups/plug_in.pdb
@@ -0,0 +1,449 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub plugins_query {
+    $blurb = 'Queries the plug-in database for its contents.';
+    $help  = 'This procedure queries the contents of the plug-in database.';
+
+    &andy_pdb_misc('1998');
+
+    @inargs = (
+       { name  => 'search_string', type => 'string', no_validate => 1,
+         desc  => 'If not an empty string then use this as a search pattern' }
+    );
+
+    @outargs = (
+       { name => 'menu_path', type => 'stringarray',
+         desc => 'The menu path of the plug-in',
+         array => { name => 'num_plugins',
+                    desc => 'The number of plug-ins' } },
+       { name => 'plugin_accelerator', type => 'stringarray',
+         desc => 'String representing keyboard accelerator (could be empty
+                  string)',
+         array => { name => 'num_plugins', no_declare => 1,
+                    desc => 'The number of plug-ins' } },
+       { name => 'plugin_location', type => 'stringarray',
+         desc => 'Location of the plug-in program',
+         array => { name => 'num_plugins', no_declare => 1,
+                    desc => 'The number of plug-ins' } },
+       { name => 'plugin_image_type', type => 'stringarray',
+         desc => 'Type of image that this plug-in will work on',
+         array => { name => 'num_plugins', no_declare => 1,
+                    desc => 'The number of plug-ins' } },
+       { name => 'plugin_install_time', type => 'int32array',
+         desc => 'Time that the plug-in was installed',
+         array => { name => 'num_plugins', no_declare => 1,
+                    desc => 'The number of plug-ins' } },
+       { name => 'plugin_real_name', type => 'stringarray',
+         desc => 'The internal name of the plug-in',
+         array => { name => 'num_plugins', no_declare => 1,
+                    desc => 'The number of plug-ins' } }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  num_plugins = gimp_plug_in_manager_query (gimp->plug_in_manager,
+                                            search_string,
+                                            &menu_path,
+                                            &plugin_accelerator,
+                                            &plugin_location,
+                                            &plugin_image_type,
+                                            &plugin_real_name,
+                                            &plugin_install_time);
+}
+CODE
+    );
+}
+
+sub plugin_domain_register {
+    $blurb = 'Registers a textdomain for localisation.';
+
+    $help = <<'HELP';
+This procedure adds a textdomain to the list of domains Gimp searches 
+for strings when translating its menu entries. There is no need to 
+call this function for plug-ins that have their strings included in 
+the 'gimp-std-plugins' domain as that is used by default. If the compiled 
+message catalog is not in the standard location, you may specify an 
+absolute path to another location. This procedure can only be called 
+in the query function of a plug-in and it has to be called before any
+procedure is installed.
+HELP
+
+    &neo_pdb_misc('2000');
+
+    @inargs = (
+       { name => 'domain_name', type => 'string',
+         desc => 'The name of the textdomain (must be unique)' },
+       { name => 'domain_path', type => 'string', no_validate => 1,
+         desc => 'The absolute path to the compiled message catalog (may be
+                  NULL)' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPlugIn *plug_in = gimp->plug_in_manager->current_plug_in;
+
+  if (plug_in && plug_in->call_mode == GIMP_PLUG_IN_CALL_QUERY)
+    {
+      gimp_plug_in_def_set_locale_domain (plug_in->plug_in_def,
+                                          domain_name, domain_path);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub plugin_help_register {
+    $blurb = "Register a help path for a plug-in.";
+
+    $help = <<HELP;
+
+This procedure registers user documentation for the calling plug-in
+with the GIMP help system. The domain_uri parameter points to the root
+directory where the plug-in help is installed. For each supported
+language there should be a file called 'gimp-help.xml' that maps the
+help IDs to the actual help files.
+HELP
+
+    &mitch_pdb_misc('2000');
+
+    @inargs = (
+       { name => 'domain_name', type => 'string',
+         desc => "The XML namespace of the plug-in's help pages" },
+       { name => 'domain_uri', type => 'string',
+         desc => "The root URI of the plug-in's help pages" }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpPlugIn *plug_in = gimp->plug_in_manager->current_plug_in;
+
+  if (plug_in && plug_in->call_mode == GIMP_PLUG_IN_CALL_QUERY)
+    {
+      gimp_plug_in_def_set_help_domain (plug_in->plug_in_def,
+                                        domain_name, domain_uri);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub plugin_menu_register {
+    $blurb = "Register an additional menu path for a plug-in procedure.";
+
+    $help = <<HELP;
+This procedure installs an additional menu entry for the given procedure.
+HELP
+
+    &mitch_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'procedure_name', type => 'string', non_empty => 1,
+         desc => 'The procedure for which to install the menu path' },
+       { name => 'menu_path', type => 'string',
+         desc => "The procedure's additional menu path" }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpPlugIn *plug_in = gimp->plug_in_manager->current_plug_in;
+
+  if (plug_in)
+    {
+      gchar *canonical = gimp_canonicalize_identifier (procedure_name);
+      success = gimp_plug_in_menu_register (plug_in, canonical, menu_path);
+      g_free (canonical);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub plugin_menu_branch_register {
+    $blurb = "Register a sub-menu.";
+
+    $help = <<HELP;
+This procedure installs a sub-menu which does not belong to any procedure.
+The menu-name should be the untranslated menu label. GIMP will look up the
+translation in the textdomain registered for the plug-in.
+HELP
+
+    &mitch_pdb_misc('2005', '2.4');
+
+    @inargs = (
+       { name => 'menu_path', type => 'string',
+         desc => "The sub-menu's menu path" },
+       { name => 'menu_name', type => 'string',
+         desc => 'The name of the sub-menu' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpPlugIn *plug_in = gimp->plug_in_manager->current_plug_in;
+
+  if (plug_in)
+    {
+      gimp_plug_in_manager_add_menu_branch (gimp->plug_in_manager,
+                                            plug_in->file, menu_path, menu_name);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub plugin_icon_register {
+    $blurb = "Register an icon for a plug-in procedure.";
+
+    $help = <<HELP;
+This procedure installs an icon for the given procedure.
+HELP
+
+    &mitch_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'procedure_name', type => 'string', wrap => 1, non_empty => 1,
+         desc => 'The procedure for which to install the icon' },
+        { name => 'icon_type', type => 'enum GimpIconType',
+          desc => 'The type of the icon' },
+       { name => 'icon_data', type => 'int8array',
+         desc => "The procedure's icon. The format depends on the
+                   'icon_type' parameter",
+         array => { name => 'icon_data_length', type => '1 <= int32',
+                     desc => "The length of 'icon-data'" } }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpPlugIn *plug_in = gimp->plug_in_manager->current_plug_in;
+
+  if (plug_in && plug_in->call_mode == GIMP_PLUG_IN_CALL_QUERY)
+    {
+      GimpPlugInProcedure *proc;
+      gchar               *canonical;
+
+      canonical = gimp_canonicalize_identifier (procedure_name);
+
+      proc = gimp_plug_in_procedure_find (plug_in->plug_in_def->procedures,
+                                          canonical);
+
+      g_free (canonical);
+
+      if (proc)
+        gimp_plug_in_procedure_set_icon (proc, icon_type,
+                                         icon_data, icon_data_length);
+      else
+        success = FALSE;
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub plugin_set_pdb_error_handler {
+    $blurb = "Sets an error handler for procedure calls.";
+
+    $help = <<HELP;
+This procedure changes the way that errors in procedure calls are
+handled. By default GIMP will raise an error dialog if a procedure
+call made by a plug-in fails. Using this procedure the plug-in can
+change this behavior. If the error handler is set to
+%GIMP_PDB_ERROR_HANDLER_PLUGIN, then the plug-in is responsible for
+calling gimp_get_pdb_error() and handling the error whenever one if
+its procedure calls fails. It can do this by displaying the error
+message or by forwarding it in its own return values.
+HELP
+
+    &neo_pdb_misc('2008', '2.6');
+
+    @inargs = (
+       { name => 'handler', type => 'enum GimpPDBErrorHandler',
+         desc => "Who is responsible for handling procedure call errors" }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpPlugIn *plug_in = gimp->plug_in_manager->current_plug_in;
+
+  if (plug_in)
+    {
+      gimp_plug_in_set_error_handler (plug_in, handler);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub plugin_get_pdb_error_handler {
+    $blurb = "Retrieves the active error handler for procedure calls.";
+
+    $help = <<HELP;
+This procedure retrieves the currently active error handler for
+procedure calls made by the calling plug-in. See
+gimp_plugin_set_pdb_error_handler() for details.
+HELP
+
+    &neo_pdb_misc('2008', '2.6');
+
+    @outargs = (
+       { name => 'handler', type => 'enum GimpPDBErrorHandler',
+         desc => "Who is responsible for handling procedure call errors" }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpPlugIn *plug_in = gimp->plug_in_manager->current_plug_in;
+
+  if (plug_in)
+    {
+      handler = gimp_plug_in_get_error_handler (plug_in);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub plugin_enable_precision {
+    $blurb = "Switches this plug-in to using the real bit depth of drawables.";
+
+    $help = <<HELP;
+Switches this plug-in to using the real bit depth of drawables. This
+setting can only be enabled, and not disabled again during the
+lifetime of the plug-in. Using gimp_drawable_get_buffer(),
+gimp_drawable_get_shadow_buffer() or gimp_drawable_get_format() will
+automatically call this function.
+HELP
+
+    &mitch_pdb_misc('2012', '2.10');
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpPlugIn *plug_in = gimp->plug_in_manager->current_plug_in;
+
+  if (plug_in)
+    {
+      gimp_plug_in_enable_precision (plug_in);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub plugin_precision_enabled {
+    $blurb = "Whether this plug-in is using the real bit depth of drawables.";
+
+    $help = <<HELP;
+Returns whether this plug-in is using the real bit depth of drawables,
+which can be more than 8 bits per channel.
+HELP
+
+    &mitch_pdb_misc('2012', '2.10');
+
+    @outargs = (
+       { name => 'enabled', type => 'boolean',
+         desc => "Whether precision is enabled" }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpPlugIn *plug_in = gimp->plug_in_manager->current_plug_in;
+
+  if (plug_in)
+    {
+      enabled = gimp_plug_in_precision_enabled (plug_in);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+@headers = qw(<string.h>
+              <stdlib.h>
+              "libgimpbase/gimpbase.h"
+              "core/gimp.h"
+              "plug-in/gimpplugin.h"
+              "plug-in/gimpplugindef.h"
+              "plug-in/gimppluginmanager.h"
+              "plug-in/gimppluginmanager-menu-branch.h"
+              "plug-in/gimppluginmanager-query.h"
+              "plug-in/gimppluginprocedure.h");
+
+@procs = qw(plugins_query
+           plugin_domain_register
+            plugin_help_register
+            plugin_menu_register
+            plugin_menu_branch_register
+            plugin_icon_register
+            plugin_set_pdb_error_handler
+            plugin_get_pdb_error_handler
+            plugin_enable_precision
+            plugin_precision_enabled);
+
+%exports = (app => [@procs], lib => [@procs[1,2,3,4,5,6,7,8,9]]);
+
+$desc = 'Plug-in';
+$doc_title = 'gimpplugin';
+$doc_short_desc = 'Functions useful for plug-ins, e.g. registration and progress indicators.';
+$doc_long_desc = 'Functions useful for plug-ins, e.g. registration and progress indicators.';
+
+1;
diff --git a/pdb/groups/plug_in_compat.pdb b/pdb/groups/plug_in_compat.pdb
new file mode 100644
index 0000000..e7dabfa
--- /dev/null
+++ b/pdb/groups/plug_in_compat.pdb
@@ -0,0 +1,4966 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub plug_in_alienmap2 {
+    $blurb = 'Alter colors in various psychedelic ways';
+
+    $help = <<'HELP';
+No help yet. Just try it and you'll see!
+HELP
+
+    &std_pdb_compat('gegl:alien-map');
+    $date = '2013';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'redfrequency', type => '0 <= float <= 20',
+         desc => 'Red/hue component frequency factor' },
+       { name => 'redangle', type => '0 <= float <= 360',
+         desc => 'Red/hue component angle factor (0-360)' },
+       { name => 'greenfrequency', type => '0 <= float <= 20',
+         desc => 'Green/saturation component frequency factor' },
+       { name => 'greenangle', type => '0 <= float <= 360',
+         desc => 'Green/saturation component angle factor (0-360)' },
+       { name => 'bluefrequency', type => '0 <= float <= 20',
+         desc => 'Blue/luminance component frequency factor' },
+       { name => 'blueangle', type => '0 <= float <= 360',
+         desc => 'Blue/luminance component angle factor (0-360)' },
+       { name => 'colormodel', type => '0 <= int8 <= 1',
+         desc => 'Color model { RGB-MODEL (0), HSL-MODEL (1) }' },
+       { name => 'redmode', type => '0 <= int8 <= 1',
+         desc => 'Red/hue application mode { TRUE, FALSE }' },
+       { name => 'greenmode', type => '0 <= int8 <= 1',
+         desc => 'Green/saturation application mode { TRUE, FALSE }' },
+       { name => 'bluemode', type => '0 <= int8 <= 1',
+         desc => 'Blue/luminance application mode { TRUE, FALSE }' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node =
+        gegl_node_new_child (NULL,
+                             "operation", "gegl:alien-map",
+                            "color-model",      (gint)     colormodel,
+                             "cpn-1-frequency",  (gdouble)  redfrequency,
+                             "cpn-2-frequency",  (gdouble)  greenfrequency,
+                             "cpn-3-frequency",  (gdouble)  bluefrequency,
+                             "cpn-1-phaseshift", (gdouble)  redangle,
+                             "cpn-2-phaseshift", (gdouble)  greenangle,
+                             "cpn-3-phaseshift", (gdouble)  blueangle,
+                             "cpn-1-keep",       (gboolean) !redmode,
+                             "cpn-2-keep",       (gboolean) !greenmode,
+                             "cpn-3-keep",       (gboolean) !bluemode,
+                             NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Alien Map"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_antialias {
+    $blurb = 'Antialias using the Scale3X edge-extrapolation algorithm';
+
+    $help = <<'HELP';
+No more help.
+HELP
+
+    &std_pdb_compat('gegl:antialias');
+    $date = '2013';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node =
+        gegl_node_new_child (NULL,
+                             "operation", "gegl:antialias",
+                             NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Antialias"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_apply_canvas {
+    $blurb = 'Add a canvas texture to the image';
+
+    $help = <<'HELP';
+This function applies a canvas texture map to the drawable.
+HELP
+
+    &std_pdb_compat('gegl:texturize-canvas');
+    $date = '2014';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'direction', type => '0 <= int32 <= 3',
+         desc => 'Light direction (0 - 3)' },
+       { name => 'depth', type => '1 <= int32 <= 50',
+         desc => 'Texture depth (1 - 50)' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node =
+        gegl_node_new_child (NULL,
+                             "operation", "gegl:texturize-canvas",
+                             "direction", direction,
+                             "depth",     depth,
+                             NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Apply Canvas"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_applylens {
+    $blurb = 'Simulate an elliptical lens over the image';
+
+    $help = <<'HELP';
+This plug-in uses Snell's law to draw an ellipsoid lens over the image.
+HELP
+
+    &std_pdb_compat('gegl:apply-lens');
+    $date = '2014';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'refraction', type => '1.0 <= float <= 100.0',
+         desc => 'Lens refraction index' },
+       { name => 'keep_surroundings', type => 'boolean',
+         desc => 'Keep lens surroundings' },
+       { name => 'set_background', type => 'boolean',
+         desc => 'Set lens surroundings to BG value' },
+       { name => 'set_transparent', type => 'boolean', dead => 1,
+         desc => 'Set lens surroundings transparent' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GimpRGB    color;
+      GeglColor *gegl_color;
+      GeglNode  *node;
+
+      if (set_background)
+        gimp_context_get_background (context, &color);
+      else
+        gimp_rgba_set (&color, 0.0, 0.0, 0.0, 0.0);
+
+      gegl_color = gimp_gegl_color_new (&color);
+
+      node = gegl_node_new_child (NULL,
+                                 "operation",         "gegl:apply-lens",
+                                 "refraction-index",  refraction,
+                                 "keep-surroundings", keep_surroundings,
+                                 "background-color",  gegl_color,
+                                 NULL);
+
+      g_object_unref (gegl_color);
+
+      node = wrap_in_selection_bounds (node, drawable);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Apply Lens"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_autocrop {
+    $blurb = 'Remove empty borders from the image';
+
+    $help = <<'HELP';
+Remove empty borders from the image.
+HELP
+
+    &std_pdb_misc;
+    $date = '1997';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image',
+         desc => 'Input image)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error))
+    {
+      gint x, y, width, height;
+      gint off_x, off_y;
+
+      gimp_pickable_auto_shrink (GIMP_PICKABLE (drawable),
+                                 0, 0,
+                                 gimp_item_get_width  (GIMP_ITEM (drawable)),
+                                 gimp_item_get_height (GIMP_ITEM (drawable)),
+                                 &x, &y, &width, &height);
+
+      gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y);
+      x += off_x;
+      y += off_y;
+
+      gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_RESIZE,
+                                   _("Autocrop image"));
+
+      if (x          < 0                             ||
+          y          < 0                             ||
+          x + width  > gimp_image_get_width  (image) ||
+          y + height > gimp_image_get_height (image))
+        {
+          /*
+           * partially outside the image area, we need to
+           * resize the image to be able to crop properly.
+           */
+          gimp_image_resize (image, context, width, height, -x, -y, NULL);
+
+          x = y = 0;
+        }
+
+      gimp_image_crop (image, context, GIMP_FILL_TRANSPARENT,
+                       x, y, width, height, TRUE);
+
+      gimp_image_undo_group_end (image);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_autocrop_layer {
+    $blurb = 'Crop the active layer based on empty borders of the input drawable';
+
+    $help = <<'HELP';
+Crop the active layer of the input "image" based on empty borders of the input "drawable".
+\n\nThe input drawable serves as a base for detecting cropping extents (transparency or background color), 
and is not necessarily the cropped layer (the current active layer).
+HELP
+
+    &std_pdb_misc;
+    $date = '1997';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image',
+         desc => 'Input image)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error))
+    {
+      GimpLayer *layer = gimp_image_get_active_layer (image);
+      gint       x, y, width, height;
+
+      if (layer)
+        {
+          switch (gimp_pickable_auto_shrink (GIMP_PICKABLE (drawable),
+                                             0, 0,
+                                             gimp_item_get_width  (GIMP_ITEM (drawable)),
+                                             gimp_item_get_height (GIMP_ITEM (drawable)),
+                                             &x, &y, &width, &height))
+            {
+            case GIMP_AUTO_SHRINK_SHRINK:
+              gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_ITEM_RESIZE,
+                                           _("Autocrop layer"));
+
+              gimp_item_resize (GIMP_ITEM (layer),
+                                context, GIMP_FILL_TRANSPARENT,
+                                width, height, -x, -y);
+
+              gimp_image_undo_group_end (image);
+             break;
+
+           default:
+             break;
+            }
+       }
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_autostretch_hsv {
+    $blurb = 'Stretch contrast to cover the maximum possible range';
+
+    $help = <<'HELP';
+This simple plug-in does an automatic contrast stretch.  For each
+channel in the image, it finds the minimum and maximum values... it
+uses those values to stretch the individual histograms to the full
+contrast range.  For some images it may do just what you want; for
+others it may be total crap :).  This version differs from Contrast
+Autostretch in that it works in HSV space, and preserves hue.
+HELP
+
+    &std_pdb_compat('gegl:stretch-contrast-hsv');
+    $date = '2013';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node =
+        gegl_node_new_child (NULL,
+                             "operation",   "gegl:stretch-contrast-hsv",
+                             NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Stretch Contrast HSV"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_bump_map {
+    $blurb = 'Create an embossing effect using a bump map';
+
+    $help = <<'HELP';
+This plug-in uses the algorithm described by John Schlag,
+"Fast Embossing Effects on Raster Image Data" in
+Graphics GEMS IV (ISBN 0-12-336155-9).
+It takes a drawable to be applied as a bump
+map to another image and produces a nice embossing effect.
+HELP
+
+    &std_pdb_compat('gegl:bump-map');
+    $date = '2015';
+
+    @inargs = (
+  { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+    desc => 'The run mode' },
+  { name => 'image', type => 'image', dead => 1,
+    desc => 'Input image (unused)' },
+  { name => 'drawable', type => 'drawable',
+    desc => 'Input drawable' },
+  { name => 'bumpmap', type => 'drawable',
+    desc => 'Bump map drawable' },
+  { name => 'azimuth', type => '0.0 <= float <= 360.0',
+    desc => 'Azimuth' },
+  { name => 'elevation', type => '0.5 <= float <= 90.0',
+    desc => 'Elevation' },
+  { name => 'depth', type => '1 <= int32 <= 65',
+    desc => 'Depth' },
+  { name => 'xofs', type => 'int32',
+    desc => 'X offset' },
+  { name => 'yofs', type => 'int32',
+    desc => 'Y offset' },
+  { name => 'waterlevel', type => '0.0 <= float <= 1.0',
+    desc => 'Level that full transparency should represent' },
+  { name => 'ambient', type => '0.0 <= float <= 1.0',
+    desc => 'Ambient lighting factor' },
+  { name => 'compensate', type => 'boolean',
+    desc => 'Compensate for darkening' },
+  { name => 'invert', type => 'boolean',
+    desc => 'Invert bumpmap' },
+  { name => 'type', type => '0 <= int32 <= 3',
+    desc => 'Type of map { LINEAR (0), SPHERICAL (1), SINUSOIDAL (2) }' }
+    );
+
+    %invoke = (
+  code => <<'CODE'
+{
+  success = bump_map (drawable,
+                      bumpmap,
+                      azimuth,
+                      elevation,
+                      depth,
+                      xofs,
+                      yofs,
+                      waterlevel,
+                      ambient,
+                      compensate,
+                      invert,
+                      type,
+                      FALSE,
+                      progress,
+                      error);
+}
+CODE
+    );
+}
+
+sub plug_in_bump_map_tiled {
+    $blurb = 'Create an embossing effect using a tiled image as a bump map';
+
+    $help = <<'HELP';
+This plug-in uses the algorithm described by John Schlag,
+"Fast Embossing Effects on Raster Image Data" in
+Graphics GEMS IV (ISBN 0-12-336155-9).
+It takes a drawable to be tiled and applied as a bump map
+to another image and produces a nice embossing effect.
+HELP
+
+    &std_pdb_compat('gegl:bump-map');
+    $date = '2015';
+
+    @inargs = (
+  { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+    desc => 'The run mode' },
+  { name => 'image', type => 'image', dead => 1,
+    desc => 'Input image (unused)' },
+  { name => 'drawable', type => 'drawable',
+    desc => 'Input drawable' },
+  { name => 'bumpmap', type => 'drawable',
+    desc => 'Bump map drawable' },
+  { name => 'azimuth', type => '0.0 <= float <= 360.0',
+    desc => 'Azimuth' },
+  { name => 'elevation', type => '0.5 <= float <= 90.0',
+    desc => 'Elevation' },
+  { name => 'depth', type => '1 <= int32 <= 65',
+    desc => 'Depth' },
+  { name => 'xofs', type => 'int32',
+    desc => 'X offset' },
+  { name => 'yofs', type => 'int32',
+    desc => 'Y offset' },
+  { name => 'waterlevel', type => '0.0 <= float <= 1.0',
+    desc => 'Level that full transparency should represent' },
+  { name => 'ambient', type => '0.0 <= float <= 1.0',
+    desc => 'Ambient lighting factor' },
+  { name => 'compensate', type => 'boolean',
+    desc => 'Compensate for darkening' },
+  { name => 'invert', type => 'boolean',
+    desc => 'Invert bumpmap' },
+  { name => 'type', type => '0 <= int32 <= 3',
+    desc => 'Type of map { LINEAR (0), SPHERICAL (1), SINUSOIDAL (2) }' }
+    );
+
+    %invoke = (
+  code => <<'CODE'
+{
+  success = bump_map (drawable,
+                      bumpmap,
+                      azimuth,
+                      elevation,
+                      depth,
+                      xofs,
+                      yofs,
+                      waterlevel,
+                      ambient,
+                      compensate,
+                      invert,
+                      type,
+                      TRUE,
+                      progress,
+                      error);
+}
+CODE
+    );
+}
+
+sub plug_in_c_astretch {
+    $blurb = 'Stretch contrast to cover the maximum possible range';
+
+    $help = <<'HELP';
+This simple plug-in does an automatic contrast stretch.  For each
+channel in the image, it finds the minimum and maximum values... it
+uses those values to stretch the individual histograms to the full
+contrast range.  For some images it may do just what you want; for
+others it may not work that well.
+HELP
+
+    &std_pdb_compat('gegl:stretch-contrast');
+    $date = '2013';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node =
+        gegl_node_new_child (NULL,
+                             "operation",   "gegl:stretch-contrast",
+                             "keep-colors", (gboolean) FALSE,
+                             NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Stretch Contrast"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_colors_channel_mixer {
+    $blurb = 'Alter colors by mixing RGB Channels';
+
+    $help = <<'HELP';
+This plug-in mixes the RGB channels.
+HELP
+
+    &std_pdb_compat('gegl:channel-mixer');
+    $date = '2013';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'monochrome', type => '0 <= int32 <= 1',
+         desc => 'Monochrome { TRUE, FALSE }' },
+       { name => 'rr_gain', type => '-2 <= float <= 2',
+         desc => 'Set the red gain for the red channel' },
+       { name => 'rg_gain', type => '-2 <= float <= 2',
+         desc => 'Set the green gain for the red channel' },
+       { name => 'rb_gain', type => '-2 <= float <= 2',
+         desc => 'Set the blue gain for the red channel' },
+       { name => 'gr_gain', type => '-2 <= float <= 2',
+         desc => 'Set the red gain for the green channel' },
+       { name => 'gg_gain', type => '-2 <= float <= 2',
+         desc => 'Set the green gain for the green channel' },
+       { name => 'gb_gain', type => '-2 <= float <= 2',
+         desc => 'Set the blue gain for the green channel' },
+       { name => 'br_gain', type => '-2 <= float <= 2',
+         desc => 'Set the red gain for the blue channel' },
+       { name => 'bg_gain', type => '-2 <= float <= 2',
+         desc => 'Set the green gain for the blue channel' },
+       { name => 'bb_gain', type => '-2 <= float <= 2',
+         desc => 'Set the blue gain for the blue channel' },
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node = NULL;
+
+      if (monochrome)
+        {
+          node = gegl_node_new_child (NULL,
+                                      "operation", "gegl:mono-mixer",
+                                      "red",       rr_gain,
+                                      "green",     rg_gain,
+                                      "blue",      rb_gain,
+                                      NULL);
+         }
+       else
+         {
+           node = gegl_node_new_child (NULL,
+                                       "operation", "gegl:channel-mixer",
+                                       "rr-gain",   rr_gain,
+                                       "rg-gain",   rg_gain,
+                                       "rb-gain",   rb_gain,
+                                       "gr-gain",   gr_gain,
+                                       "gg-gain",   gg_gain,
+                                       "gb-gain",   gb_gain,
+                                       "br-gain",   br_gain,
+                                       "bg-gain",   bg_gain,
+                                       "bb-gain",   bb_gain,
+                                        NULL);
+         }
+
+       gimp_drawable_apply_operation (drawable, progress,
+                                      C_("undo-type", "Channel Mixer"),
+                                      node);
+       g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_colortoalpha {
+    $blurb = 'Convert a specified color to transparency';
+
+    $help = <<'HELP';
+This replaces as much of a given color as possible in each pixel with
+a corresponding amount of alpha, then readjusts the color accordingly.
+HELP
+
+    &std_pdb_misc;
+    $date = '1999';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'color', type => 'color',
+         desc => 'Color to remove' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      /* XXX: fixme disable for gray, and add alpha when needed */
+
+      GeglColor *gegl_color = gimp_gegl_color_new (&color);
+      GeglNode  *node =
+        gegl_node_new_child (NULL,
+                             "operation", "gegl:color-to-alpha",
+                             "color",     gegl_color,
+                             NULL);
+      g_object_unref (gegl_color);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Color to Alpha"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_convmatrix {
+    $blurb = 'Apply a generic 5x5 convolution matrix';
+
+    $help = <<'HELP';
+Apply a generic 5x5 convolution matrix.
+HELP
+
+    &std_pdb_compat('gegl:convolution-matrix');
+    $date = '2014';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'matrix', type => 'floatarray',
+         desc => 'The 5x5 convolution matrix',
+         array => { name => 'argc_matrix',
+                    desc => 'The number of elements in the following array, must always be 25' } },
+       { name => 'alpha_alg', type => 'boolean',
+         desc => 'Enable weighting by alpha channel' },
+       { name => 'divisor', type => 'float',
+         desc => 'Divisor' },
+       { name => 'offset', type => 'float',
+         desc => 'Offset' },
+       { name => 'channels', type => 'int32array',
+         desc => 'Mask of the channels to be filtered',
+         array => { name => 'argc_channels',
+                    desc => 'The number of elements in following array, must always be 5' } },
+       { name => 'bmode', type => '0 <= int32 <= 2',
+         desc => 'Mode for treating image borders { EXTEND (0), WRAP (1), CLEAR (2) }' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (argc_matrix != 25)
+    {
+      g_set_error (error, GIMP_PDB_ERROR, GIMP_PDB_ERROR_INVALID_ARGUMENT,
+                   _("Array 'matrix' has only %d members, must have 25"),
+                   argc_matrix);
+      success = FALSE;
+    }
+
+  if (success && argc_channels != 5)
+    {
+      g_set_error (error, GIMP_PDB_ERROR, GIMP_PDB_ERROR_INVALID_ARGUMENT,
+                   _("Array 'channels' has only %d members, must have 5"),
+                   argc_channels);
+      success = FALSE;
+    }
+
+  if (success &&
+      gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode        *node;
+      GeglAbyssPolicy  border = GEGL_ABYSS_CLAMP;
+      gboolean         r      = channels[1];
+      gboolean         g      = channels[2];
+      gboolean         b      = channels[3];
+      gboolean         a      = channels[4];
+
+      if (gimp_drawable_is_gray (drawable))
+        {
+          r = channels[0];
+          g = channels[0];
+          b = channels[0];
+        }
+
+      switch (bmode)
+        {
+        case 0: border = GEGL_ABYSS_CLAMP; break;
+        case 1: border = GEGL_ABYSS_LOOP; break;
+        case 2: border = GEGL_ABYSS_NONE; break;
+        }
+
+      node = gegl_node_new_child (NULL,
+                                  "operation",    "gegl:convolution-matrix",
+                                  "a1",           matrix[0],
+                                  "a2",           matrix[1],
+                                  "a3",           matrix[2],
+                                  "a4",           matrix[3],
+                                  "a5",           matrix[4],
+                                  "b1",           matrix[5],
+                                  "b2",           matrix[6],
+                                  "b3",           matrix[7],
+                                  "b4",           matrix[8],
+                                  "b5",           matrix[9],
+                                  "c1",           matrix[10],
+                                  "c2",           matrix[11],
+                                  "c3",           matrix[12],
+                                  "c4",           matrix[13],
+                                  "c5",           matrix[14],
+                                  "d1",           matrix[15],
+                                  "d2",           matrix[16],
+                                  "d3",           matrix[17],
+                                  "d4",           matrix[18],
+                                  "d5",           matrix[19],
+                                  "e1",           matrix[20],
+                                  "e2",           matrix[21],
+                                  "e3",           matrix[22],
+                                  "e4",           matrix[23],
+                                  "e5",           matrix[24],
+                                  "divisor",      divisor,
+                                  "offset",       offset,
+                                  "red",          r,
+                                  "green",        g,
+                                  "blue",         b,
+                                  "alpha",        a,
+                                  "normalize",    FALSE,
+                                  "alpha-weight", alpha_alg,
+                                  "border",       border,
+                                  NULL);
+
+      node = wrap_in_gamma_cast (node, drawable);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Convolution Matrix"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_cubism {
+    $blurb = 'Convert the image into randomly rotated square blobs';
+
+    $help = <<'HELP';
+Convert the image into randomly rotated square blobs.
+HELP
+
+    &std_pdb_compat('gegl:cubism');
+    $date = '2013';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'tile_size', type => '0.0 <= float <= 100.0',
+         desc => 'Average diameter of each tile (in pixels)' },
+       { name => 'tile_saturation', type => '0.0 <= float <= 10.0',
+         desc => 'Expand tiles by this amount' },
+       { name => 'bg_color', type => '0 <= int32 <= 1',
+         desc => 'Background color { BLACK (0), BG (1) }' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GimpRGB    color;
+      GeglColor *gegl_color;
+      GeglNode  *node;
+
+      if (bg_color)
+        {
+          gimp_context_get_background (context, &color);
+          gimp_rgb_set_alpha (&color, 0.0);
+        }
+      else
+        {
+          gimp_rgba_set (&color, 0.0, 0.0, 0.0, 0.0);
+        }
+
+      gegl_color = gimp_gegl_color_new (&color);
+
+      node = gegl_node_new_child (NULL,
+                                  "operation",       "gegl:cubism",
+                                  "tile-size",       tile_size,
+                                  "tile-saturation", tile_saturation,
+                                  "bg-color",        gegl_color,
+                                  NULL);
+      g_object_unref (gegl_color);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Cubism"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_deinterlace {
+    $blurb = 'Fix images where every other row is missing';
+
+    $help = <<'HELP';
+Deinterlace is useful for processing images from video capture
+cards. When only the odd or even fields get captured, deinterlace can
+be used to interpolate between the existing fields to correct this.
+HELP
+
+    &std_pdb_compat('gegl:deinterlace');
+    $date = '2014';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'evenodd', type => '0 <= int32 <= 1',
+         desc => 'Which lines to keep { KEEP-ODD (0), KEEP-EVEN (1)' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node;
+
+      node = gegl_node_new_child (NULL,
+                                  "operation",   "gegl:deinterlace",
+                                  "keep",        evenodd ? 0 : 1,
+                                  "orientation", 0, /* HORIZONTAL */
+                                  "size",        1,
+                                  NULL);
+
+      node = wrap_in_gamma_cast (node, drawable);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Deinterlace"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_diffraction {
+    $blurb = 'Generate diffraction patterns';
+
+    $help = <<'HELP';
+Help?  What help?
+HELP
+
+    &std_pdb_compat('gegl:diffraction-patterns');
+    $date = '2015';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'lam_r', type => '0.0 <= float <= 20.0',
+         desc => 'Light frequency (red)' },
+       { name => 'lam_g', type => '0.0 <= float <= 20.0',
+         desc => 'Light frequency (green)' },
+       { name => 'lam_b', type => '0.0 <= float <= 20.0',
+         desc => 'Light frequency (blue)' },
+       { name => 'contour_r', type => '0.0 <= float <= 10.0',
+         desc => 'Number of contours (red)' },
+       { name => 'contour_g', type => '0.0 <= float <= 10.0',
+         desc => 'Number of contours (green)' },
+       { name => 'contour_b', type => '0.0 <= float <= 10.0',
+         desc => 'Number of contours (blue)' },
+       { name => 'edges_r', type => '0.0 <= float <= 1.0',
+         desc => 'Number of sharp edges (red)' },
+       { name => 'edges_g', type => '0.0 <= float <= 1.0',
+         desc => 'Number of sharp edges (green)' },
+       { name => 'edges_b', type => '0.0 <= float <= 1.0',
+         desc => 'Number of sharp edges (blue)' },
+        { name => 'brightness', type => '0.0 <= float <= 1.0',
+         desc => 'Brightness and shifting/fattening of contours' },
+       { name => 'scattering', type => '0.0 <= float <= 100.0',
+         desc => 'Scattering (Speed vs. quality)' },
+       { name => 'polarization', type => '-1.0 <= float <= 1.0',
+         desc => 'Polarization' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node;
+      gint      x, y, width, height;
+
+      gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height);
+
+      node = gegl_node_new_child (NULL,
+                                  "operation",       "gegl:diffraction-patterns",
+                                  "red-frequency",   lam_r,
+                                  "green-frequency", lam_g,
+                                  "blue-frequency",  lam_b,
+                                  "red-contours",    contour_r,
+                                  "green-contours",  contour_g,
+                                  "blue-contours",   contour_b,
+                                  "red-sedges",      edges_r,
+                                  "green-sedges",    edges_g,
+                                  "blue-sedges",     edges_b,
+                                  "brightness",      brightness,
+                                  "scattering",      scattering,
+                                  "polarization",    polarization,
+                                  "width",           width,
+                                  "height",          height,
+                                  NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Diffraction Patterns"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_displace {
+    $blurb = 'Displace pixels as indicated by displacement maps';
+
+    $help = <<'HELP';
+Displaces the contents of the specified drawable by the amounts specified
+by 'amount-x' and 'amount-y' multiplied by the luminance of corresponding
+pixels in the 'displace-map' drawables.
+HELP
+
+    &std_pdb_compat('gegl:displace');
+    $date = '2015';
+
+    @inargs = (
+  { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+    desc => 'The run mode' },
+  { name => 'image', type => 'image', dead => 1,
+    desc => 'Input image (unused)' },
+  { name => 'drawable', type => 'drawable',
+    desc => 'Input drawable' },
+  { name => 'amount_x', type => '-500.0 <= float <= 500.0',
+    desc => 'Displace multiplier for x direction' },
+  { name => 'amount_y', type => '-500.0 <= float <= 500.0',
+    desc => 'Displace multiplier for y direction' },
+  { name => 'do_x', type => 'boolean',
+    desc => 'Displace in x direction ?' },
+  { name => 'do_y', type => 'boolean',
+    desc => 'Displace in y direction ?' },
+  { name => 'displace_map_x', type => 'drawable',
+    desc => 'Displacement map for x direction' },
+  { name => 'displace_map_y', type => 'drawable',
+    desc => 'Displacement map for y direction' },
+  { name => 'displace_type', type => '1 <= int32 <= 3',
+    desc => 'Edge behavior { WRAP (1), SMEAR (2), BLACK (3) }' }
+    );
+
+    %invoke = (
+  code => <<'CODE'
+{
+  success = displace (drawable,
+                      amount_x,
+                      amount_y,
+                      do_x,
+                      do_y,
+                      displace_map_x,
+                      displace_map_y,
+                      displace_type,
+                      0,
+                      progress,
+                      error);
+}
+CODE
+    );
+}
+
+sub plug_in_displace_polar {
+    $blurb = 'Displace pixels as indicated by displacement maps';
+
+    $help = <<'HELP';
+Just like plug-in-displace but working in polar coordinates.
+The drawable is whirled and pinched according to the map.
+HELP
+
+    &std_pdb_compat('gegl:displace');
+    $date = '2015';
+
+    @inargs = (
+  { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+    desc => 'The run mode' },
+  { name => 'image', type => 'image', dead => 1,
+    desc => 'Input image (unused)' },
+  { name => 'drawable', type => 'drawable',
+    desc => 'Input drawable' },
+  { name => 'amount_x', type => '-500.0 <= float <= 500.0',
+    desc => 'Displace multiplier for radial direction' },
+  { name => 'amount_y', type => '-500.0 <= float <= 500.0',
+    desc => 'Displace multiplier for tangent direction' },
+  { name => 'do_x', type => 'boolean',
+    desc => 'Displace in radial direction ?' },
+  { name => 'do_y', type => 'boolean',
+    desc => 'Displace in tangent direction ?' },
+  { name => 'displace_map_x', type => 'drawable',
+    desc => 'Displacement map for radial direction' },
+  { name => 'displace_map_y', type => 'drawable',
+    desc => 'Displacement map for tangent direction' },
+  { name => 'displace_type', type => '1 <= int32 <= 3',
+    desc => 'Edge behavior { WRAP (1), SMEAR (2), BLACK (3) }' }
+    );
+
+    %invoke = (
+  code => <<'CODE'
+{
+  success = displace (drawable,
+                      amount_x,
+                      amount_y,
+                      do_x,
+                      do_y,
+                      displace_map_x,
+                      displace_map_y,
+                      displace_type,
+                      1,
+                      progress,
+                      error);
+}
+CODE
+    );
+}
+
+sub plug_in_edge {
+    $blurb = 'Several simple methods for detecting edges';
+
+    $help = <<'HELP';
+Perform edge detection on the contents of the specified drawable.
+AMOUNT is an arbitrary constant, WRAPMODE is like displace plug-in
+(useful for tileable image). EDGEMODE sets the kind of matrix transform
+applied to the pixels, SOBEL was the method used in older versions.
+HELP
+
+    &std_pdb_compat('gegl:edge');
+    $date = '2015';
+
+    @inargs = (
+        { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+          desc => 'The run mode' },
+        { name => 'image', type => 'image', dead => 1,
+          desc => 'Input image (unused)' },
+        { name => 'drawable', type => 'drawable',
+          desc => 'Input drawable' },
+        { name => 'amount', type => '1.0 <= float <= 10.0',
+          desc => 'Edge detection amount' },
+        { name => 'warpmode', type => '0 <= int32 <= 3',
+          desc => 'Edge detection behavior { NONE (0), WRAP (1), SMEAR (2), BLACK (3) }' },
+        { name => 'edgemode', type => '0 <= int32 <= 5',
+          desc => 'Edge detection algorithm { SOBEL (0), PREWITT (1), GRADIENT (2), ROBERTS (3), 
DIFFERENTIAL (4), LAPLACE (5) }' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode        *node;
+      GeglAbyssPolicy  border_behavior = GEGL_ABYSS_NONE;
+
+      switch (warpmode)
+        {
+       case 0:
+         border_behavior = GEGL_ABYSS_NONE;
+         break;
+
+       case 1:
+         border_behavior = GEGL_ABYSS_LOOP;
+         break;
+
+        case 2:
+         border_behavior = GEGL_ABYSS_CLAMP;
+         break;
+
+        case 3:
+         border_behavior = GEGL_ABYSS_BLACK;
+         break;
+       }
+
+      node = gegl_node_new_child (NULL,
+                                  "operation",       "gegl:edge",
+                                  "algorithm",       edgemode,
+                                  "amount",          amount,
+                                  "border-behavior", border_behavior,
+                                  NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Edge"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_engrave {
+    $blurb = 'Simulate an antique engraving';
+
+    $help = <<'HELP';
+Creates a black-and-white 'engraved' version of an image as seen in
+old illustrations.
+HELP
+
+    &std_pdb_compat('gegl:engrave');
+    $date = '2014';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'height', type => '2 <= int32 <= 16',
+         desc => 'Resolution in pixels' },
+       { name => 'limit', type => 'boolean',
+         desc => 'Limit line width' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node;
+
+      node = gegl_node_new_child (NULL,
+                                  "operation",   "gegl:engrave",
+                                  "row-height",  height,
+                                  "limit",       limit,
+                                  NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Engrave"),
+                                     node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_exchange {
+    $blurb = 'Swap one color with another';
+
+    $help = <<'HELP';
+Exchange one color with another, optionally setting a threshold to
+convert from one shade to another.
+HELP
+
+    &std_pdb_compat('gegl:color-exchange');
+    $date = '2014';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+
+       { name => 'from_red', type => 'int8',
+         desc => 'Red value (from)' },
+       { name => 'from_green', type => 'int8',
+         desc => 'Green value (from)' },
+       { name => 'from_blue', type => 'int8',
+         desc => 'Blue value (from)' },
+       { name => 'to_red', type => 'int8',
+         desc => 'Red value (to)' },
+       { name => 'to_green', type => 'int8',
+         desc => 'Green value (to)' },
+       { name => 'to_blue', type => 'int8',
+         desc => 'Blue value (to)' },
+       { name => 'red_threshold', type => 'int8',
+         desc => 'Red threshold' },
+       { name => 'green_threshold', type => 'int8',
+         desc => 'Green threshold' },
+       { name => 'blue_threshold', type => 'int8',
+         desc => 'Blue threshold' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GimpRGB    from;
+      GimpRGB    to;
+      GeglColor *gegl_from;
+      GeglColor *gegl_to;
+      GeglNode  *node;
+
+      gimp_rgb_set_uchar (&from, from_red, from_green, from_blue);
+      gimp_rgb_set_uchar (&to,   to_red,   to_green,   to_blue);
+
+      gegl_from = gimp_gegl_color_new (&from);
+      gegl_to   = gimp_gegl_color_new (&to);
+
+      node = gegl_node_new_child (NULL,
+                                  "operation",       "gegl:color-exchange",
+                                 "from-color",      gegl_from,
+                                  "to-color",        gegl_to,
+                                 "red-threshold",   red_threshold   / 255.0,
+                                 "green-threshold", green_threshold / 255.0,
+                                 "blue-threshold",  blue_threshold  / 255.0,
+                                  NULL);
+
+      g_object_unref (gegl_from);
+      g_object_unref (gegl_to);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Color Exchange"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_flarefx {
+    $blurb = 'Add a lens flare effect';
+
+    $help = <<'HELP';
+Adds a lens flare effects. Makes your image look like it was snapped
+with a cheap camera with a lot of lens :)
+HELP
+
+    &std_pdb_compat('gegl:lens-flare');
+    $date = '2015';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'pos_x', type => 'int32',
+         desc => 'X-Position' },
+       { name => 'pos_y', type => 'int32',
+         desc => 'Y-Position' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node;
+      gint      width  = gimp_item_get_width  (GIMP_ITEM (drawable));
+      gint      height = gimp_item_get_height (GIMP_ITEM (drawable));
+      gdouble   x      = (gdouble) pos_x / (gdouble) width;
+      gdouble   y      = (gdouble) pos_y / (gdouble) height;
+
+      node = gegl_node_new_child (NULL,
+                                  "operation", "gegl:lens-flare",
+                                  "pos-x",     x,
+                                  "pos-y",     y,
+                                  NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Lens Flare"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_gauss {
+    $blurb = 'Simplest, most commonly used way of blurring';
+
+    $help = <<'HELP';
+Applies a gaussian blur to the drawable, with specified radius of affect.
+The standard deviation of the normal distribution used to modify pixel
+values is calculated based on the supplied radius.
+Horizontal and vertical blurring can be independently invoked by specifying
+only one to run. The 'method' parameter is ignored.
+HELP
+
+    &std_pdb_compat('gegl:gaussian-blur');
+    $date = '2014';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'horizontal', type => '0.0 <= float <= 500.0',
+         desc => 'Horizontal radius of gaussian blur (in pixels' },
+       { name => 'vertical', type => '0.0 <= float <= 500.0',
+         desc => 'Vertical radius of gaussian blur (in pixels' },
+       { name => 'method', type => '0 <= int32 <= 1', dead => 1,
+         desc => 'Blur method { IIR (0), RLE (1) }' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  success = gaussian_blur (drawable, horizontal, vertical, progress, error);
+}
+CODE
+    );
+}
+
+sub plug_in_gauss_iir {
+    $blurb = 'Apply a gaussian blur';
+
+    $help = <<'HELP';
+Applies a gaussian blur to the drawable, with specified radius of affect.
+The standard deviation of the normal distribution used to modify pixel
+values is calculated based on the supplied radius. Horizontal and vertical
+blurring can be independently invoked by specifying only one to run.
+HELP
+
+    &std_pdb_compat('gegl:gaussian-blur');
+    $date = '2014';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'radius', type => '0.0 <= float <= 500.0',
+         desc => 'Radius of gaussian blur (in pixels' },
+       { name => 'horizontal', type => 'boolean',
+         desc => 'Blur in horizontal direction' },
+       { name => 'vertical', type => 'boolean',
+         desc => 'Blur in vertical direction' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  success = gaussian_blur (drawable,
+                           horizontal ? radius : 0.0,
+                           vertical   ? radius : 0.0,
+                           progress, error);
+}
+CODE
+    );
+}
+
+sub plug_in_gauss_iir2 {
+    $blurb = 'Apply a gaussian blur';
+
+    $help = <<'HELP';
+Applies a gaussian blur to the drawable, with specified radius of affect.
+The standard deviation of the normal distribution used to modify pixel
+values is calculated based on the supplied radius. Horizontal and vertical
+blurring can be independently invoked by specifying only one to run.
+HELP
+
+    &std_pdb_compat('gegl:gaussian-blur');
+    $date = '2014';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'horizontal', type => '0.0 <= float <= 500.0',
+         desc => 'Horizontal radius of gaussian blur (in pixels' },
+       { name => 'vertical', type => '0.0 <= float <= 500.0',
+         desc => 'Vertical radius of gaussian blur (in pixels' },
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  success = gaussian_blur (drawable, horizontal, vertical, progress, error);
+}
+CODE
+    );
+}
+
+sub plug_in_gauss_rle {
+    $blurb = 'Apply a gaussian blur';
+
+    $help = <<'HELP';
+Applies a gaussian blur to the drawable, with specified radius of affect.
+The standard deviation of the normal distribution used to modify pixel
+values is calculated based on the supplied radius. Horizontal and vertical
+blurring can be independently invoked by specifying only one to run.
+HELP
+
+    &std_pdb_compat('gegl:gaussian-blur');
+    $date = '2014';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'radius', type => '0.0 <= float <= 500.0',
+         desc => 'Radius of gaussian blur (in pixels' },
+       { name => 'horizontal', type => 'boolean',
+         desc => 'Blur in horizontal direction' },
+       { name => 'vertical', type => 'boolean',
+         desc => 'Blur in vertical direction' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  success = gaussian_blur (drawable,
+                           horizontal ? radius : 0.0,
+                           vertical   ? radius : 0.0,
+                           progress, error);
+}
+CODE
+    );
+}
+
+sub plug_in_gauss_rle2 {
+    $blurb = 'Apply a gaussian blur';
+
+    $help = <<'HELP';
+Applies a gaussian blur to the drawable, with specified radius of affect.
+The standard deviation of the normal distribution used to modify pixel
+values is calculated based on the supplied radius. Horizontal and vertical
+blurring can be independently invoked by specifying only one to run.
+HELP
+
+    &std_pdb_compat('gegl:gaussian-blur');
+    $date = '2014';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'horizontal', type => '0.0 <= float <= 500.0',
+         desc => 'Horizontal radius of gaussian blur (in pixels' },
+       { name => 'vertical', type => '0.0 <= float <= 500.0',
+         desc => 'Vertical radius of gaussian blur (in pixels' },
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  success = gaussian_blur (drawable, horizontal, vertical, progress, error);
+}
+CODE
+    );
+}
+
+sub plug_in_glasstile {
+    $blurb = 'Simulate distortion caused by square glass tiles';
+
+    $help = <<'HELP';
+Divide the image into square glassblocks in which the image is
+refracted.
+HELP
+
+    &std_pdb_compat('gegl:tile-glass');
+    $date = '2014';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'tilex', type => '10 <= int32 <= 500',
+         desc => 'Tile width' },
+       { name => 'tiley', type => '10 <= int32 <= 500',
+         desc => 'Tile height' },
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node;
+
+      node = gegl_node_new_child (NULL,
+                                  "operation",   "gegl:tile-glass",
+                                  "tile-width",  tilex,
+                                  "tile-height", tiley,
+                                  NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Glass Tile"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_hsv_noise {
+    $blurb = 'Randomize hue, saturation and value independently';
+
+    $help = <<'HELP';
+Scattering pixel values in HSV space
+HELP
+
+    &std_pdb_compat('gegl:noise-hsv');
+    $date = '2013';
+
+    @inargs = (
+        { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+          desc => 'The run mode' },
+        { name => 'image', type => 'image', dead => 1,
+          desc => 'Input image (unused)' },
+        { name => 'drawable', type => 'drawable',
+          desc => 'Input drawable' },
+        { name => 'holdness', type => '1 <= int32 <= 8',
+          desc => 'Convolution strength' },
+        { name => 'hue_distance', type => '0 <= int32 <= 180',
+          desc => 'Scattering of hue angle' },
+        { name => 'saturation_distance', type => '0 <= int32 <= 255',
+          desc => 'Distribution distance on saturation axis' },
+        { name => 'value_distance', type => '0 <= int32 <= 255',
+          desc => 'Distribution distance on value axis' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode  *node;
+
+      gdouble saturation = saturation_distance / 255.0;
+      gdouble value = value_distance / 255.0;
+
+
+      node = gegl_node_new_child (NULL,
+                                  "operation",           "gegl:noise-hsv",
+                                  "holdness",            (gint)    holdness,
+                                  "hue-distance",        (gdouble) hue_distance,
+                                  "saturation-distance", (gdouble) saturation,
+                                  "value-distance",      (gdouble) value,
+                                  NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Noise HSV"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_icc_profile_info {
+    $blurb = "Retrieve information about an image's color profile";
+
+    $help = <<'HELP';
+This procedure returns information about the RGB color profile
+attached to an image. If no RGB color profile is attached, sRGB is
+assumed.
+HELP
+
+    &neo_pdb_misc;
+    $date = '2015';
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'Input image' }
+    );
+
+    @outargs = (
+        { name => 'profile_name', type => 'string',
+          desc => 'Name' },
+        { name => 'profile_desc', type => 'string',
+          desc => 'Description' },
+        { name => 'profile_info', type => 'string',
+          desc => 'Info' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpColorProfile *profile;
+
+  profile = gimp_color_managed_get_color_profile (GIMP_COLOR_MANAGED (image));
+
+  if (profile)
+    {
+      profile_name = g_strdup (gimp_color_profile_get_model (profile));
+      profile_desc = g_strdup (gimp_color_profile_get_description (profile));
+      profile_info = g_strdup (gimp_color_profile_get_summary (profile));
+    }
+}
+CODE
+    );
+}
+
+sub plug_in_icc_profile_file_info {
+    $blurb = "Retrieve information about a color profile";
+
+    $help = <<'HELP';
+This procedure returns information about an ICC color profile on disk.
+HELP
+
+    &neo_pdb_misc;
+    $date = '2015';
+
+    @inargs = (
+        { name => 'profile', type => 'string',
+          desc => 'Filename of an ICC color profile', allow_non_utf8 => 1 }
+    );
+
+    @outargs = (
+        { name => 'profile_name', type => 'string',
+          desc => 'Name' },
+        { name => 'profile_desc', type => 'string',
+          desc => 'Description' },
+        { name => 'profile_info', type => 'string',
+          desc => 'Info' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GFile *file = g_file_new_for_path (profile);
+
+  if (file)
+    {
+      GimpColorProfile *p;
+
+      p = gimp_color_profile_new_from_file (file, error);
+      g_object_unref (file);
+
+      if (p)
+        {
+          profile_name = g_strdup (gimp_color_profile_get_model (p));
+          profile_desc = g_strdup (gimp_color_profile_get_description (p));
+          profile_info = g_strdup (gimp_color_profile_get_summary (p));
+
+          g_object_unref (p);
+        }
+      else
+        success = FALSE;
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_icc_profile_apply {
+    $blurb = "Apply a color profile on the image";
+
+    $help = <<'HELP';
+This procedure transform from the image's color profile (or the
+default RGB profile if none is set) to the given ICC color
+profile. Only RGB color profiles are accepted. The profile is then set
+on the image using the 'icc-profile' "parasite.
+HELP
+
+    &neo_pdb_misc;
+    $date = '2015';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+        { name => 'image', type => 'image',
+          desc => 'Input image' },
+        { name => 'profile', type => 'string',
+          desc => 'Filename of an ICC color profile', allow_non_utf8 => 1 },
+        { name => 'intent', type => 'enum GimpColorRenderingIntent',
+          desc => 'Rendering intent' },
+        { name => 'bpc', type => 'boolean',
+          desc => 'Black point compensation' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_image_is_not_base_type (image, GIMP_GRAY, error))
+    {
+      GimpColorProfile *p = NULL;
+
+      if (profile)
+        {
+          GFile *file = g_file_new_for_path (profile);
+
+          if (file)
+            {
+              p = gimp_color_profile_new_from_file (file, error);
+
+              if (! p)
+                success = FALSE;
+
+              g_object_unref (file);
+           }
+          else
+            {
+              success = FALSE;
+            }
+        }
+      else if (image->gimp->config->color_management->rgb_profile)
+        {
+          p = gimp_color_config_get_rgb_color_profile (image->gimp->config->color_management,
+                                                       error);
+
+          if (! p)
+            success = FALSE;
+        }
+
+      if (success)
+        {
+          if (! p)
+            p = gimp_image_get_builtin_color_profile (image);
+
+          success = gimp_image_convert_color_profile (image, p, intent, bpc,
+                                                      progress, error);
+       }
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_icc_profile_apply_rgb {
+    $blurb = "Apply default RGB color profile on the image";
+
+    $help = <<'HELP';
+This procedure transform from the image's color profile (or the
+default RGB profile if none is set) to the configured default RGB
+color profile.  The profile is then set on the image using the
+'icc-profile' parasite.  If no RGB color profile is configured, sRGB
+is assumed and the parasite is unset.
+HELP
+
+    &neo_pdb_misc;
+    $date = '2015';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+        { name => 'image', type => 'image',
+          desc => 'Input image' },
+        { name => 'intent', type => 'enum GimpColorRenderingIntent',
+          desc => 'Rendering intent' },
+        { name => 'bpc', type => 'boolean',
+          desc => 'Black point compensation' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_image_is_not_base_type (image, GIMP_GRAY, error))
+    {
+      GimpColorProfile *p = NULL;
+
+      if (image->gimp->config->color_management->rgb_profile)
+        {
+          p = gimp_color_config_get_rgb_color_profile (image->gimp->config->color_management,
+                                                       error);
+
+          if (! p)
+            success = FALSE;
+        }
+
+      if (success)
+        {
+          if (! p)
+            p = gimp_image_get_builtin_color_profile (image);
+
+          success = gimp_image_convert_color_profile (image, p, intent, bpc,
+                                                      progress, error);
+       }
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_icc_profile_set {
+    $blurb = "Set a color profile on the image";
+
+    $help = <<'HELP';
+This procedure sets the user-configured RGB profile on an image using
+the 'icc-profile' parasite. This procedure does not do any color
+conversion.
+HELP
+
+    &neo_pdb_misc;
+    $date = '2015';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+        { name => 'image', type => 'image',
+          desc => 'Input image' },
+        { name => 'profile', type => 'string',
+          desc => 'Filename of an ICC color profile', allow_non_utf8 => 1 }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_image_is_not_base_type (image, GIMP_GRAY, error))
+    {
+      GimpColorProfile *p = NULL;
+
+      if (profile)
+        {
+          GFile *file = g_file_new_for_path (profile);
+
+          if (file)
+            {
+              p = gimp_color_profile_new_from_file (file, error);
+
+              if (! p)
+                success = FALSE;
+
+              g_object_unref (file);
+           }
+          else
+            success = FALSE;
+        }
+      else if (image->gimp->config->color_management->rgb_profile)
+        {
+          p = gimp_color_config_get_rgb_color_profile (image->gimp->config->color_management,
+                                                       error);
+
+          if (! p)
+            success = FALSE;
+        }
+
+      if (success)
+        {
+          gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_MISC,
+                                       _("Set color profile"));
+
+          if (gimp_image_set_color_profile (image, p, error))
+            gimp_image_parasite_detach (image, "icc-profile-name");
+          else
+            success = FALSE;
+
+          gimp_image_undo_group_end (image);
+
+          if (! success)
+           gimp_image_undo (image);
+
+          if (p)
+            g_object_unref (p);
+       }
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_icc_profile_set_rgb {
+    $blurb = "Set the default RGB color profile on the image";
+
+    $help = <<'HELP';
+This procedure sets the user-configured RGB profile on an image using
+the 'icc-profile' parasite. If no RGB profile is configured, sRGB is
+assumed and the parasite is unset. This procedure does not do any
+color conversion.
+HELP
+
+    &neo_pdb_misc;
+    $date = '2015';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+        { name => 'image', type => 'image',
+          desc => 'Input image' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_image_is_not_base_type (image, GIMP_GRAY, error))
+    {
+      GimpColorProfile *p = NULL;
+
+      if (image->gimp->config->color_management->rgb_profile)
+        {
+          p = gimp_color_config_get_rgb_color_profile (image->gimp->config->color_management,
+                                                       error);
+
+          if (! p)
+            success = FALSE;
+        }
+
+      if (success)
+        {
+          gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_MISC,
+                                       _("Set color profile"));
+
+          if (gimp_image_set_color_profile (image, p, error))
+            gimp_image_parasite_detach (image, "icc-profile-name");
+          else
+            success = FALSE;
+
+          gimp_image_undo_group_end (image);
+
+          if (! success)
+           gimp_image_undo (image);
+
+          if (p)
+            g_object_unref (p);
+       }
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_illusion {
+    $blurb = 'Superimpose many altered copies of the image';
+
+    $help = <<'HELP';
+Produce illusion.
+HELP
+
+    &std_pdb_compat('gegl:illusion');
+    $date = '2014';
+
+    @inargs = (
+        { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+          desc => 'The run mode' },
+        { name => 'image', type => 'image', dead => 1,
+          desc => 'Input image (unused)' },
+        { name => 'drawable', type => 'drawable',
+          desc => 'Input drawable' },
+        { name => 'division', type => '0 <= int32 <= 64',
+          desc => 'The number of divisions' },
+        { name => 'type', type => '0 <= int32 <= 1',
+          desc => 'Illusion type { TYPE1 (0), TYPE2 (1) }' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node =
+        gegl_node_new_child (NULL,
+                             "operation",     "gegl:illusion",
+                             "division",      (gint) division,
+                             "illusion-type", (gint) type,
+                             NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Illusion"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_laplace {
+    $blurb = 'High-resolution edge detection';
+
+    $help = <<'HELP';
+This plug-in creates one-pixel wide edges from the
+image, with the value proportional to the gradient. 
+It uses the Laplace operator (a 3x3 kernel with -8 
+in the middle). The image has to be laplacered to 
+get useful results, a gauss_iir with 1.5 - 5.0 
+depending on the noise in the image is best.
+HELP
+
+    &std_pdb_compat('gegl:edge-laplace');
+    $date = '2013';
+
+    @inargs = (
+        { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+          desc => 'The run mode' },
+        { name => 'image', type => 'image', dead => 1,
+          desc => 'Input image (unused)' },
+        { name => 'drawable', type => 'drawable',
+          desc => 'Input drawable' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node =
+        gegl_node_new_child (NULL,
+                             "operation", "gegl:edge-laplace",
+                             NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Laplace"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_lens_distortion {
+    $blurb = 'Corrects lens distortion';
+
+    $help = <<'HELP';
+Corrects barrel or pincushion lens distortion.
+HELP
+
+    &std_pdb_compat('gegl:lens-distortion');
+    $date = '2013';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'offset_x', type => '-100 <= float <= 100',
+         desc => 'Effect centre offset in X' },
+       { name => 'offset_y', type => '-100 <= float <= 100',
+         desc => 'Effect centre offset in Y' },
+       { name => 'main_adjust', type => '-100 <= float <= 100',
+         desc => 'Amount of second-order distortion' },
+       { name => 'edge_adjust', type => '-100 <= float <= 100',
+         desc => 'Amount of fourth-order distortion' },
+       { name => 'rescale', type => '-100 <= float <= 100',
+         desc => 'Rescale overall image size' },
+       { name => 'brighten', type => '-100 <= float <= 100',
+         desc => 'Adjust brightness in corners' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode  *node = NULL;
+      GimpRGB    color;
+      GeglColor *gegl_color;
+
+      gimp_context_get_background (context, &color);
+
+      if (gimp_drawable_has_alpha (drawable))
+        {
+          gimp_rgb_set_alpha (&color, 0.0);
+        }
+      else
+        {
+          gimp_rgb_set_alpha (&color, 1.0);
+        }
+
+      gegl_color = gimp_gegl_color_new (&color);
+
+      node =  gegl_node_new_child (NULL,
+                                   "operation", "gegl:lens-distortion",
+                                   "main",       (gdouble) main_adjust,
+                                   "edge",       (gdouble) edge_adjust,
+                                   "zoom",       (gdouble) rescale,
+                                   "x-shift",    (gdouble) offset_x,
+                                   "y-shift",    (gdouble) offset_y,
+                                   "brighten",   (gdouble) brighten,
+                                   "background", gegl_color,
+                                   NULL);
+
+      g_object_unref (gegl_color);
+
+      node = wrap_in_selection_bounds (node, drawable);
+ 
+      gimp_drawable_apply_operation (drawable, progress,
+                                    C_("undo-type", "Lens Distortion"),
+                                    node);
+      g_object_unref (node);
+
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_make_seamless {
+    $blurb = 'Alters edges to make the image seamlessly tileable';
+
+    $help = <<'HELP';
+This plug-in creates a seamless tileable from the input drawable.
+HELP
+
+    &std_pdb_compat('gegl:tile-seamless');
+    $date = '2013';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node =
+        gegl_node_new_child (NULL,
+                             "operation", "gegl:tile-seamless",
+                             NULL);
+
+      node = wrap_in_selection_bounds (node, drawable);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Tile Seamless"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_maze {
+    $blurb = 'Draw a labyrinth';
+
+    $help = <<'HELP';
+Generates a maze using either the depth-first search method or Prim's
+algorithm.  Can make tileable mazes too.
+HELP
+
+    &std_pdb_compat('gegl:maze');
+    $date = '2015';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'width', type => '1 <= int16 <= 1024',
+         desc => 'Width of the passages' },
+       { name => 'height', type => '1 <= int16 <= 1024',
+         desc => 'Height of the passages' },
+       { name => 'tileable', type => '0 <= int8 <= 1',
+         desc => 'Tileable maze? (TRUE or FALSE)' },
+       { name => 'algorithm', type => '0 <= int8 <= 1',
+         desc => 'Generation algorithm (0 = DEPTH FIRST, 1 = PRIM\'S ALGORITHM)' },
+       { name => 'seed', type => 'int32',
+         desc => 'Random Seed' },
+       { name => 'multiple', type => 'int16', dead => 1,
+         desc => 'Multiple (use 57)' },
+       { name => 'offset', type => 'int16', dead => 1,
+         desc => 'Offset (use 1)' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode  *node;
+      GeglColor *fg_color;
+      GeglColor *bg_color;
+      GimpRGB    color;
+
+      gimp_context_get_foreground (context, &color);
+      fg_color = gimp_gegl_color_new (&color);
+
+      gimp_context_get_background (context, &color);
+      bg_color = gimp_gegl_color_new (&color);
+
+      node =  gegl_node_new_child (NULL,
+                                   "operation",      "gegl:maze",
+                                   "x",              width,
+                                   "y",              height,
+                                   "algorithm-type", algorithm,
+                                   "tileable",       tileable,
+                                   "seed",           seed,
+                                   "fg-color",       fg_color,
+                                   "bg-color",       bg_color,
+                                   NULL);
+
+      g_object_unref (fg_color);
+      g_object_unref (bg_color);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Maze"),
+                                    node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_mblur {
+    $blurb = 'Simulate movement using directional blur';
+
+    $help = <<'HELP';
+This plug-in simulates the effect seen when
+photographing a moving object at a slow shutter
+speed. Done by adding multiple displaced copies.
+HELP
+
+    &std_pdb_compat('gegl:motion-blur-linear, -zoom, -cirular');
+    $date = '2013';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'type', type => '0 <= int32 <= 2',
+         desc => 'Type of motion blur { LINEAR (0), RADIAL (1), ZOOM (2) }' },
+       { name => 'length', type => 'float',
+         desc => 'Length' },
+       { name => 'angle', type => '0 <= float <= 360',
+         desc => 'Angle' },
+       { name => 'center_x', type => 'float',
+         desc => 'Center X' },
+       { name => 'center_y', type => 'float',
+         desc => 'Center Y' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node   = NULL;
+      gint      width  = gimp_item_get_width  (GIMP_ITEM (drawable));
+      gint      height = gimp_item_get_height (GIMP_ITEM (drawable));
+
+      center_x /= (gdouble) width;
+      center_y /= (gdouble) height;
+
+      if (angle > 180.0)
+        angle -= 360.0;
+
+      if (type == 0)
+        {
+          node =  gegl_node_new_child (NULL,
+                                       "operation", "gegl:motion-blur-linear",
+                                       "length",    length,
+                                       "angle",     angle,
+                                        NULL);
+        }
+      else if (type == 1)
+        {
+          node =  gegl_node_new_child (NULL,
+                                       "operation", "gegl:motion-blur-circular",
+                                       "center-x",  center_x,
+                                       "center-y",  center_y,
+                                       "angle",     angle,
+                                        NULL);
+        }
+      else if (type == 2)
+        {
+         gdouble factor = CLAMP (length / 256.0, 0.0, 1.0);
+
+          node =  gegl_node_new_child (NULL,
+                                       "operation", "gegl:motion-blur-zoom",
+                                       "center-x",  center_x,
+                                       "center-y",  center_y,
+                                       "factor",    factor,
+                                        NULL);
+        }
+
+      if (node != NULL)
+        {
+          gimp_drawable_apply_operation (drawable, progress,
+                                         C_("undo-type", "Motion Blur"),
+                                         node);
+          g_object_unref (node);
+        }
+      else
+        success = FALSE;
+
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_mblur_inward {
+    $blurb = 'Simulate movement using directional blur';
+
+    $help = <<'HELP';
+This procedure is equivalent to plug-in-mblur but 
+performs the zoom blur inward instead of outward.
+HELP
+
+    &std_pdb_compat('gegl:motion-blur-linear, -zoom, -cirular');
+    $date = '2013';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'type', type => '0 <= int32 <= 2',
+         desc => 'Type of motion blur { LINEAR (0), RADIAL (1), ZOOM (2) }' },
+       { name => 'length', type => 'float',
+         desc => 'Length' },
+       { name => 'angle', type => '0 <= float <= 360',
+         desc => 'Angle' },
+       { name => 'center_x', type => 'float',
+         desc => 'Center X' },
+       { name => 'center_y', type => 'float',
+         desc => 'Center Y' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node   = NULL;
+      gint      width  = gimp_item_get_width  (GIMP_ITEM (drawable));
+      gint      height = gimp_item_get_height (GIMP_ITEM (drawable));
+
+      center_x /= (gdouble) width;
+      center_y /= (gdouble) height;
+
+      if (type == 0)
+        {
+          node =  gegl_node_new_child (NULL,
+                                       "operation", "gegl:motion-blur-linear",
+                                       "length",    length,
+                                       "angle",     angle,
+                                        NULL);
+        }
+      else if (type == 1)
+        {
+          node =  gegl_node_new_child (NULL,
+                                       "operation", "gegl:motion-blur-circular",
+                                       "center-x",  center_x,
+                                       "center-y",  center_y,
+                                       "angle",     angle,
+                                        NULL);
+        }
+      else if (type == 2)
+        {
+          gdouble factor = CLAMP (-length / (256.0 - length), -10.0, 0.0);
+
+          node =  gegl_node_new_child (NULL,
+                                       "operation", "gegl:motion-blur-zoom",
+                                       "center-x",  center_x,
+                                       "center-y",  center_y,
+                                       "factor",    factor,
+                                        NULL);
+        }
+
+      if (node != NULL)
+        {
+          gimp_drawable_apply_operation (drawable, progress,
+                                         C_("undo-type", "Motion Blur"),
+                                         node);
+          g_object_unref (node);
+        }
+      else
+        success = FALSE;
+
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_mosaic {
+    $blurb = 'Convert the image into irregular tiles';
+
+    $help = <<'HELP';
+Mosaic is a filter which transforms an image into
+what appears to be a mosaic, composed of small primitives,
+each of constant color and of an approximate size.
+HELP
+
+    &std_pdb_compat('gegl:mosaic');
+    $date = '2013';
+
+    @inargs = (
+        { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+          desc => 'The run mode' },
+        { name => 'image', type => 'image', dead => 1,
+          desc => 'Input image (unused)' },
+        { name => 'drawable', type => 'drawable',
+          desc => 'Input drawable' },
+        { name => 'tile_size', type => '1 <= float <= 1000',
+          desc => 'Average diameter of each tile (in pixels)' },
+        { name => 'tile_height', type => '1 <= float <= 1000',
+          desc => 'Apparent height of each tile (in pixels)' },
+        { name => 'tile_spacing', type => '0.1 <= float <= 1000',
+          desc => 'Inter_tile spacing (in pixels)' },
+        { name => 'tile_neatness', type => '0 <= float <= 1.0',
+          desc => 'Deviation from perfectly formed tiles' },
+        { name => 'tile_allow_split', type => '0 <= int32 <= 1',
+          desc => 'Allows splitting tiles at hard edges' },
+        { name => 'light_dir', type => '0 <= float <= 360',
+          desc => 'Direction of light_source (in degrees)' },
+        { name => 'color_variation', type => '0.0 <= float <= 1.0',
+          desc => 'Magnitude of random color variations' },
+        { name => 'antialiasing', type => '0 <= int32 <= 1',
+          desc => 'Enables smoother tile output at the cost of speed' },
+        { name => 'color_averaging', type => '0 <= int32 <= 1',
+          desc => 'Tile color based on average of subsumed pixels' },
+        { name => 'tile_type', type => '0 <= int32 <= 3',
+          desc => 'Tile geometry { SQUARES (0), HEXAGONS (1), OCTAGONS (2), TRIANGLES (3) }' },
+        { name => 'tile_surface', type => '0 <= int32 <= 1',
+          desc => 'Surface characteristics { SMOOTH (0), ROUGH (1) }' },
+        { name => 'grout_color', type => '0 <= int32 <= 1',
+          desc => 'Grout color (black/white or fore/background) { BW (0), FG-BG (1) }' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglColor *fg_color;
+      GeglColor *bg_color;
+      GeglNode  *node;
+
+      if (grout_color)
+        {
+          GimpRGB fgcolor, bgcolor;
+
+          gimp_context_get_background (context, &bgcolor);
+          bg_color = gimp_gegl_color_new (&bgcolor);
+
+          gimp_context_get_foreground (context, &fgcolor);
+          fg_color = gimp_gegl_color_new (&fgcolor);
+        }
+      else
+        {
+          /* sic */
+          fg_color = gegl_color_new ("white");
+          bg_color = gegl_color_new ("black");
+        }
+
+      node = gegl_node_new_child (NULL,
+                 "operation",        "gegl:mosaic",
+                 "tile-size",        (gdouble)  tile_size,
+                 "tile-height",      (gdouble)  tile_height,
+                 "tile-spacing",     (gdouble)  tile_spacing,
+                 "tile-neatness",    (gdouble)  tile_neatness,
+                 "tile-allow-split", (gboolean) tile_allow_split,
+                 "light-dir",        (gdouble)  light_dir,
+                 "color-variation",  (gfloat)   color_variation,
+                 "antialiasing",     (gboolean) antialiasing,
+                 "color-averaging",  (gboolean) color_averaging,
+                 "tile-type",        (gint)     tile_type,
+                 "tile-surface",     (gboolean) tile_surface,
+                 "light-color",      fg_color,
+                 "joints-color",     bg_color,
+                 NULL);
+
+      g_object_unref (fg_color);
+      g_object_unref (bg_color);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Mosaic"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_nova {
+    $blurb = 'Add a starburst to the image';
+
+    $help = <<'HELP';
+This plug-in produces an effect like a supernova burst. The amount of
+the light effect is approximately in proportion to 1/r, where r is the
+distance from the center of the star.
+HELP
+
+    &std_pdb_compat('gegl:supernova');
+    $date = '2014';
+
+    @inargs = (
+        { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+          desc => 'The run mode' },
+        { name => 'image', type => 'image', dead => 1,
+          desc => 'Input image (unused)' },
+        { name => 'drawable', type => 'drawable',
+          desc => 'Input drawable' },
+        { name => 'xcenter', type => 'int32',
+          desc => 'X coordinates of the center of supernova' },
+        { name => 'ycenter', type => 'int32',
+          desc => 'Y coordinates of the center of supernova' },
+       { name => 'color', type => 'color',
+         desc => 'Color of supernova' },
+        { name => 'radius', type => '1 <= int32 <= 3000',
+          desc => 'Radius of supernova' },
+        { name => 'nspoke', type => '1 <= int32 <= 1024',
+          desc => 'Number of spokes' },
+        { name => 'randomhue', type => '0 <= int32 <= 360',
+          desc => 'Random hue' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode  *node;
+      GeglColor *gegl_color = gimp_gegl_color_new (&color);
+      gdouble    center_x   = (gdouble) xcenter / (gdouble) gimp_item_get_width (GIMP_ITEM (drawable));
+      gdouble    center_y   = (gdouble) ycenter / (gdouble) gimp_item_get_height (GIMP_ITEM (drawable));
+
+      node = gegl_node_new_child (NULL,
+                                  "operation",    "gegl:supernova",
+                                  "center-x",     center_x,
+                                  "center-y",     center_y,
+                                  "radius",       radius,
+                                  "spokes-count", nspoke,
+                                  "random-hue",   randomhue,
+                                  "color",        gegl_color,
+                                  "seed",         g_random_int (),
+                                  NULL);
+
+      g_object_unref (gegl_color);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Supernova"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_papertile {
+    $blurb = 'Cut image into paper tiles, and slide them';
+
+    $help = <<'HELP';
+This plug-in cuts an image into paper tiles and slides each paper tile.
+HELP
+
+    &std_pdb_compat('gegl:tile-paper');
+    $date = '2015';
+
+    @inargs = (
+        { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+          desc => 'The run mode' },
+        { name => 'image', type => 'image', dead => 1,
+          desc => 'Input image (unused)' },
+        { name => 'drawable', type => 'drawable',
+          desc => 'Input drawable' },
+        { name => 'tile_size', type => 'int32',
+          desc => 'Tile size (pixels)' },
+        { name => 'move_max', type => 'float',
+          desc => 'Max move rate (%)' },
+       { name => 'fractional_type', type => '0 <= int32 <= 2',
+         desc => 'Fractional type { BACKGROUND (0), IGNORE (1), FORCE (2) }' },
+        { name => 'wrap_around', type => 'boolean',
+          desc => 'Wrap around' },
+        { name => 'centering', type => 'boolean',
+          desc => 'Centering' },
+        { name => 'background_type', type => '0 <= int32 <= 5',
+          desc => 'Background type { TRANSPARENT (0), INVERTED (1), IMAGE (2), FG (3), BG (4), COLOR (5) }' 
},
+        { name => 'background_color', type => 'color',
+          desc => 'Background color (for background-type == 5)' },
+        { name => 'background_alpha', type => 'int32', dead => 1,
+          desc => 'Background alpha (unused)' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode  *node;
+      GimpRGB    color;
+      GeglColor *gegl_color;
+      gint       bg_type;
+
+      switch (background_type)
+        {
+        default:
+          bg_type = background_type;
+          gimp_rgba_set (&color, 0.0, 0.0, 1.0, 1.0);
+          break;
+
+        case 3:
+          bg_type = 3;
+          gimp_context_get_foreground (context, &color);
+         break;
+
+       case 4:
+          bg_type = 3;
+          gimp_context_get_background (context, &color);
+         break;
+
+       case 5:
+          bg_type = 3;
+          color = background_color;
+         break;
+        }
+
+      gegl_color = gimp_gegl_color_new (&color);
+
+      node = gegl_node_new_child (NULL,
+                                  "operation",       "gegl:tile-paper",
+                                  "tile-width",      tile_size,
+                                  "tile-height",     tile_size,
+                                  "move-rate",       move_max,
+                                  "bg-color",        gegl_color,
+                                  "centering",       centering,
+                                  "wrap-around",     wrap_around,
+                                  "background-type", bg_type,
+                                  "fractional-type", fractional_type,
+                                  NULL);
+
+      g_object_unref (gegl_color);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Paper Tile"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_pixelize {
+    $blurb = 'Simplify image into an array of solid-colored squares';
+
+    $help = <<'HELP';
+Pixelize the contents of the specified drawable with specified
+pixelizing width.
+HELP
+
+    &std_pdb_misc;
+    $date = '1997';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'pixel_width', type => '1 <= int32 <= GIMP_MAX_IMAGE_SIZE',
+         desc => 'Pixel width (the decrease in resolution)' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node =
+        gegl_node_new_child (NULL,
+                             "operation", "gegl:pixelize",
+                             "size-x",    pixel_width,
+                             "size-y",    pixel_width,
+                             NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Pixelize"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_pixelize2 {
+    $blurb = 'Simplify image into an array of solid-colored rectangles';
+
+    $help = <<'HELP';
+Pixelize the contents of the specified drawable with specified
+pixelizing width and height.
+HELP
+
+    &std_pdb_misc;
+    $date = '1997';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'pixel_width', type => '1 <= int32 <= GIMP_MAX_IMAGE_SIZE',
+         desc => 'Pixel width (the decrease in horizontal resolution)' },
+       { name => 'pixel_height', type => '1 <= int32 <= GIMP_MAX_IMAGE_SIZE',
+         desc => 'Pixel height (the decrease in vertical resolution)' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node =
+        gegl_node_new_child (NULL,
+                             "operation", "gegl:pixelize",
+                             "size-x",    pixel_width,
+                             "size-y",    pixel_height,
+                             NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Pixelize"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_plasma {
+    $blurb = 'Create a random plasma texture';
+
+    $help = <<'HELP';
+This plug-in produces plasma fractal images.
+HELP
+
+    &std_pdb_compat('gegl:plasma');
+    $date = '2013';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'seed', type => '-1 <= int32 <= G_MAXINT',
+         desc => 'Random seed' },
+       { name => 'turbulence', type => '0.0 <= float <= 7.0',
+         desc => 'The value of the turbulence' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node;
+      gint      x, y, width, height;
+
+      gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height);
+
+      node = gegl_node_new_child (NULL,
+                                  "operation",  "gegl:plasma",
+                                  "seed",       seed,
+                                  "turbulence", turbulence,
+                                  "x",          x,
+                                  "y",          y,
+                                  "width",      width,
+                                  "height",     height,
+                                  NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Plasma"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_polar_coords {
+    $blurb = 'Convert image to or from polar coordinates';
+
+    $help = <<'HELP';
+Remaps and image from rectangular coordinates to polar coordinates or
+vice versa.
+HELP
+
+    &std_pdb_misc;
+    $date = '1997';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'circle', type => '0.0 <= float <= 100.0',
+         desc => 'Circle depth in %' },
+       { name => 'angle', type => '0.0 <= float < 360.0',
+         desc => 'Offset angle' },
+       { name => 'backwards', type => 'boolean',
+         desc => 'Map backwards' },
+       { name => 'inverse', type => 'boolean',
+         desc => 'Map from top' },
+       { name => 'polrec', type => 'boolean',
+         desc => 'Polar to rectangular' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node =
+        gegl_node_new_child (NULL,
+                             "operation", "gegl:polar-coordinates",
+                             "depth",     circle,
+                             "angle",     angle,
+                             "bw",        backwards, /* XXX name */
+                             "top",       inverse,
+                             "polar",     polrec,
+                             NULL);
+
+      node = wrap_in_selection_bounds (node, drawable);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Polar Coordinates"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_randomize_hurl {
+    $blurb = 'Completely randomize a fraction of pixels';
+
+    $help = <<'HELP';
+This plug-in "hurls" randomly-valued pixels onto the selection or
+image.  You may select the percentage of pixels to modify and the
+number of times to repeat the process.
+HELP
+
+    &std_pdb_compat('gegl:noise-hurl');
+    $date = '2013';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+        { name => 'rndm_pct', type => '1.0 <= float <= 100.0',
+          desc => 'Randomization percentage' },
+        { name => 'rndm_rcount', type => '1.0 <= float <= 100.0',
+          desc => 'Repeat count' },
+        { name => 'randomize', type => 'boolean',
+          desc => 'Use random seed' },
+        { name => 'seed', type => 'int32',
+          desc => 'Seed value (used only if randomize is FALSE)' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node;
+
+      if (randomize)
+        seed = g_random_int ();
+
+      node =
+        gegl_node_new_child (NULL,
+                             "operation",  "gegl:noise-hurl",
+                             "seed",       seed,
+                             "pct-random", rndm_pct,
+                             "repeat",     (gint) rndm_rcount,
+                             NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Random Hurl"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_randomize_pick {
+    $blurb = 'Randomly interchange some pixels with neighbors';
+
+    $help = <<'HELP';
+This plug-in replaces a pixel with a random adjacent pixel.  You may
+select the percentage of pixels to modify and the number of times to
+repeat the process.
+HELP
+
+    &std_pdb_compat('gegl:noise-pick');
+    $date = '2013';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+        { name => 'rndm_pct', type => '1.0 <= float <= 100.0',
+          desc => 'Randomization percentage' },
+        { name => 'rndm_rcount', type => '1.0 <= float <= 100.0',
+          desc => 'Repeat count' },
+        { name => 'randomize', type => 'boolean',
+          desc => 'Use random seed' },
+        { name => 'seed', type => 'int32',
+          desc => 'Seed value (used only if randomize is FALSE)' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node;
+
+      if (randomize)
+        seed = g_random_int ();
+
+      node =
+        gegl_node_new_child (NULL,
+                             "operation",  "gegl:noise-pick",
+                             "seed",       seed,
+                             "pct-random", rndm_pct,
+                             "repeat",     (gint) rndm_rcount,
+                             NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Random Pick"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_randomize_slur {
+    $blurb = 'Randomly slide some pixels downward (similar to melting';
+
+    $help = <<'HELP';
+This plug-in "slurs" (melts like a bunch of icicles) an image.  You may
+select the percentage of pixels to modify and the number of times to
+repeat the process.
+HELP
+
+    &std_pdb_compat('gegl:noise-slur');
+    $date = '2013';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+        { name => 'rndm_pct', type => '1.0 <= float <= 100.0',
+          desc => 'Randomization percentage' },
+        { name => 'rndm_rcount', type => '1.0 <= float <= 100.0',
+          desc => 'Repeat count' },
+        { name => 'randomize', type => 'boolean',
+          desc => 'Use random seed' },
+        { name => 'seed', type => 'int32',
+          desc => 'Seed value (used only if randomize is FALSE)' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node;
+
+      if (randomize)
+        seed = g_random_int ();
+
+      node =
+        gegl_node_new_child (NULL,
+                             "operation",  "gegl:noise-slur",
+                             "seed",       seed,
+                             "pct-random", rndm_pct,
+                             "repeat",     (gint) rndm_rcount,
+                             NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Random Slur"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_red_eye_removal {
+    $blurb = 'Remove the red eye effect caused by camera flashes';
+
+    $help = <<'HELP';
+This procedure removes the red eye effect caused by camera flashes by
+using a percentage based red color threshold.  Make a selection
+containing the eyes, and apply the filter while adjusting the
+threshold to accurately remove the red eyes.
+HELP
+
+    &std_pdb_compat('gegl:red-eye-removal');
+    $date = '2013';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'threshold', type => '0 <= int32 <= 100',
+         desc => 'Red eye threshold in percent' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node =
+        gegl_node_new_child (NULL,
+                             "operation", "gegl:red-eye-removal",
+                             "threshold", (gdouble) (threshold - 50) / 50.0 * 0.2 + 0.4,
+                             NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Red Eye Removal"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_rgb_noise {
+    $blurb = 'Distort colors by random amounts';
+
+    $help = <<'HELP';
+Add normally distributed (zero mean) random values to image channels.
+Noise may be additive (uncorrelated) or multiplicative (correlated -
+also known as speckle noise). For color images color channels may be
+treated together or independently.
+HELP
+
+    &std_pdb_compat('gegl:noise-rgb');
+    $date = '2013';
+
+    @inargs = (
+        { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+          desc => 'The run mode' },
+        { name => 'image', type => 'image', dead => 1,
+          desc => 'Input image (unused)' },
+        { name => 'drawable', type => 'drawable',
+          desc => 'Input drawable' },
+        { name => 'independent', type => 'boolean',
+          desc => 'Noise in channels independent' },
+        { name => 'correlated', type => 'boolean',
+          desc => 'Noise correlated (i.e. multiplicative not additive)' },
+        { name => 'noise_1', type => '0.0 <= float <= 1.0',
+          desc => 'Noise in the first channel (red, gray)' },
+        { name => 'noise_2', type => '0.0 <= float <= 1.0',
+          desc => 'Noise in the second channel (green, gray_alpha)' },
+        { name => 'noise_3', type => '0.0 <= float <= 1.0',
+          desc => 'Noise in the third channel (blue)' },
+        { name => 'noise_4', type => '0.0 <= float <= 1.0',
+          desc => 'Noise in the fourth channel (alpha)' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node;
+      gdouble   r, g, b, a;
+
+      if (gimp_drawable_is_gray (drawable))
+        {
+          r = noise_1;
+          g = noise_1;
+          b = noise_1;
+          a = noise_2;
+        }
+      else
+        {
+          r = noise_1;
+          g = noise_2;
+          b = noise_3;
+          a = noise_4;
+        }
+
+      node = gegl_node_new_child (NULL,
+                                  "operation",   "gegl:noise-rgb",
+                                  "correlated",  correlated,
+                                  "independent", independent,
+                                  "red",         r,
+                                  "green",       g,
+                                  "blue",        b,
+                                  "alpha",       a,
+                                  "seed",        g_random_int_range (0, G_MAXINT),
+                                  NULL);
+
+      node = wrap_in_gamma_cast (node, drawable);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "RGB Noise"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_rotate {
+    $blurb = 'Rotates a layer or the whole image by 90, 180 or 270 degrees';
+
+    $help = <<'HELP';
+This plug-in does rotate the active layer or the whole image clockwise
+by multiples of 90 degrees.  When the whole image is chosen, the image
+is resized if necessary.
+HELP
+
+    &neo_pdb_misc;
+    $date = '2014';
+
+    @inargs = (
+        { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+          desc => 'The run mode' },
+        { name => 'image', type => 'image',
+          desc => 'Input image (unused)' },
+        { name => 'drawable', type => 'drawable',
+          desc => 'Input drawable' },
+        { name => 'angle', type => '1 <= int32 <= 3',
+          desc => 'Angle { 90 (1), 180 (2), 270 (3) } degrees' },
+        { name => 'everything', type => 'boolean',
+          desc => 'Rotate the whole image' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpRotationType rotate_type = angle - 1;
+
+  if (everything)
+    {
+      gimp_image_rotate (image, context, rotate_type, progress);
+    }
+  else if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                      GIMP_PDB_ITEM_CONTENT, error))
+    {
+      GimpItem *item = GIMP_ITEM (drawable);
+      gint      off_x, off_y;
+      gdouble   center_x, center_y;
+
+      gimp_item_get_offset (item, &off_x, &off_y);
+
+      center_x = ((gdouble) off_x + (gdouble) gimp_item_get_width  (item) / 2.0);
+      center_y = ((gdouble) off_y + (gdouble) gimp_item_get_height (item) / 2.0);
+
+      gimp_item_rotate (item, context, rotate_type, center_x, center_y,
+                        GIMP_IS_CHANNEL (drawable));
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_noisify {
+    $blurb = 'Adds random noise to image channels';
+
+    $help = <<'HELP';
+Add normally distributed random values to image channels. For color
+images each color channel may be treated together or independently.
+HELP
+
+    &std_pdb_compat('gegl:noise-rgb');
+    $date = '2013';
+
+    @inargs = (
+        { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+          desc => 'The run mode' },
+        { name => 'image', type => 'image', dead => 1,
+          desc => 'Input image (unused)' },
+        { name => 'drawable', type => 'drawable',
+          desc => 'Input drawable' },
+        { name => 'independent', type => 'boolean',
+          desc => 'Noise in channels independent' },
+        { name => 'noise_1', type => '0.0 <= float <= 1.0',
+          desc => 'Noise in the first channel (red, gray)' },
+        { name => 'noise_2', type => '0.0 <= float <= 1.0',
+          desc => 'Noise in the second channel (green, gray_alpha)' },
+        { name => 'noise_3', type => '0.0 <= float <= 1.0',
+          desc => 'Noise in the third channel (blue)' },
+        { name => 'noise_4', type => '0.0 <= float <= 1.0',
+          desc => 'Noise in the fourth channel (alpha)' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node;
+      gdouble   r, g, b, a;
+
+      if (gimp_drawable_is_gray (drawable))
+        {
+          r = noise_1;
+          g = noise_1;
+          b = noise_1;
+          a = noise_2;
+        }
+      else
+        {
+          r = noise_1;
+          g = noise_2;
+          b = noise_3;
+          a = noise_4;
+        }
+
+      node = gegl_node_new_child (NULL,
+                                  "operation",   "gegl:noise-rgb",
+                                  "correlated",  FALSE,
+                                  "independent", independent,
+                                  "red",         r,
+                                  "green",       g,
+                                  "blue",        b,
+                                  "alpha",       a,
+                                  "seed",        g_random_int_range (0, G_MAXINT),
+                                  NULL);
+
+      node = wrap_in_gamma_cast (node, drawable);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Noisify"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_sel_gauss {
+    $blurb = 'Blur neighboring pixels, but only in low-contrast areas';
+
+    $help = <<'HELP';
+This filter functions similar to the regular gaussian blur filter
+except that neighbouring pixels that differ more than the given
+maxdelta parameter will not be blended with. This way with the correct
+parameters, an image can be smoothed out without losing
+details. However, this filter can be rather slow.
+HELP
+
+    &std_pdb_compat('gegl:gaussian-blur-selective');
+    $date = '2099';
+
+    @inargs = (
+        { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+          desc => 'The run mode' },
+        { name => 'image', type => 'image', dead => 1,
+          desc => 'Input image (unused)' },
+        { name => 'drawable', type => 'drawable',
+          desc => 'Input drawable' },
+        { name => 'radius', type => '0.0 < float',
+          desc => 'Radius of gaussian blur (in pixels)' },
+        { name => 'max_delta', type => '0 <= int32 <= 255',
+          desc => 'Maximum delta' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node;
+
+      node = gegl_node_new_child (NULL,
+                                  "operation",   "gegl:gaussian-blur-selective",
+                                  "blur-radius", radius,
+                                  "max-delta",   (gdouble) max_delta / 255.0,
+                                  NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Selective Gaussian Blur"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_semiflatten {
+    $blurb = 'Replace partial transparency with the current background color';
+
+    $help = <<'HELP';
+This plug-in flattens pixels in an RGBA image that aren't completely
+transparent against the current GIMP background color.
+HELP
+
+    &std_pdb_misc;
+    $date = '1997';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error) &&
+      gimp_drawable_has_alpha (drawable))
+    {
+      GeglNode *node;
+      GimpRGB   color;
+
+      gimp_context_get_background (context, &color);
+
+      node =
+        gegl_node_new_child (NULL,
+                             "operation", "gimp:semi-flatten",
+                             "color",     &color,
+                             NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Semi-Flatten"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_shift {
+    $blurb = 'Shift each row or column of pixels by a random amount';
+
+    $help = <<'HELP';
+Shifts the pixels of the specified drawable. Each row or column will
+be displaced a random value of pixels.
+HELP
+
+    &std_pdb_compat('gegl:shift');
+    $date = '2013';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'shift_amount', type => '0 <= int32 <= 200',
+         desc => 'Shift amount' },
+       { name => 'orientation', type => '0 <= int32 <= 1',
+         desc => 'Orientation { ORIENTATION-VERTICAL (0), ORIENTATION-HORIZONTAL (1) }' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node =
+        gegl_node_new_child (NULL,
+                             "operation", "gegl:shift",
+                             "shift",     shift_amount / 2,
+                             "direction", orientation ? 0 : 1,
+                             NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Shift"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_sinus {
+    $blurb = 'Generate complex sinusoidal textures';
+    $help  = 'FIXME: sinus help',
+
+    &std_pdb_compat('gegl:sinus');
+    $date = '2014';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+        { name => 'xscale', type => '0 <= float',
+         desc => 'Scale value for x axis' },
+        { name => 'yscale', type => '0 <= float',
+         desc => 'Scale value for y axis' },
+        { name => 'complex', type => '0 <= float',
+         desc => 'Complexity factor' },
+        { name => 'seed', type => '0 <= int32',
+         desc => 'Seed value for random number generator' },
+        { name => 'tiling', type => 'boolean',
+         desc => 'If set, the pattern generated will tile' },
+        { name => 'perturb', type => 'boolean',
+         desc => 'If set, the pattern is a little more distorted...' },
+        { name => 'colors', type => '0 <= int32 <=2',
+         desc => 'where to take the colors (0=B&W,  1=fg/bg, 2=col1/col2)' },
+        { name => 'col1', type => 'color',
+         desc => 'fist color (sometimes unused)' },
+        { name => 'col2', type => 'color',
+         desc => 'second color (sometimes unused)' },
+        { name => 'alpha1', type => '0 <= float <= 1',
+         desc => 'alpha for the first color (used if the drawable has an alpha chanel)' },
+        { name => 'alpha2', type => '0 <= float <= 1',
+         desc => 'alpha for the second color (used if the drawable has an alpha chanel)' },
+        { name => 'blend', type => '0 <= int32 <= 2',
+         desc => '0=linear, 1=bilinear, 2=sinusoidal' },
+        { name => 'blend_power', type => 'float',
+         desc => 'Power used to strech the blend' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode  *node;
+      GeglColor *gegl_color1;
+      GeglColor *gegl_color2;
+      gint      x, y, width, height;
+
+      switch (colors)
+        {
+       case 0:
+         gimp_rgb_set (&col1, 0.0, 0.0, 0.0);
+         gimp_rgb_set (&col2, 1.0, 1.0, 1.0);
+          break;
+
+        case 1:
+         gimp_context_get_foreground (context, &col1);
+         gimp_context_get_background (context, &col2);
+         break;
+       }
+
+      gimp_rgb_set_alpha (&col1, alpha1);
+      gimp_rgb_set_alpha (&col2, alpha2);
+
+      gegl_color1 = gimp_gegl_color_new (&col1);
+      gegl_color2 = gimp_gegl_color_new (&col2);
+
+      gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height);
+
+      node = gegl_node_new_child (NULL,
+                                  "operation",    "gegl:sinus",
+                                  "x_scale",      xscale,
+                                  "y-scale",      yscale,
+                                  "complexity",   complex,
+                                  "seed",         seed,
+                                  "tiling",       tiling,
+                                  "perturbation", perturb,
+                                  "color1",       gegl_color1,
+                                  "color2",       gegl_color2,
+                                  "blend-mode",   blend,
+                                  "blend-power",  blend_power,
+                                 "width",        width,
+                                 "height",       height,
+                                  NULL);
+
+      g_object_unref (gegl_color1);
+      g_object_unref (gegl_color2);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Sinus"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_sobel {
+    $blurb = 'Specialized direction-dependent edge detection';
+
+    $help = <<'HELP';
+This plug-in calculates the gradient with a sobel operator. The user
+can specify which direction to use. When both directions are used, the
+result is the RMS of the two gradients; if only one direction is used,
+the result either the absolute value of the gradient, or 127 +
+gradient (if the 'keep sign' switch is on). This way, information
+about the direction of the gradient is preserved. Resulting images are
+not autoscaled."
+HELP
+
+    &std_pdb_compat('gegl:edge-sobel');
+    $date = '2014';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'horizontal', type => 'boolean',
+         desc => 'Sobel in horizontal direction' },
+       { name => 'vertical', type => 'boolean',
+         desc => 'Sobel in vertical direction' },
+       { name => 'keep_sign', type => 'boolean',
+         desc => 'Keep sign of result (one direction only)' },
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node =
+        gegl_node_new_child (NULL,
+                             "operation",  "gegl:edge-sobel",
+                             "horizontal", horizontal,
+                             "vertical",   vertical,
+                             "keep-sign",  keep_sign,
+                             NULL);
+
+      node = wrap_in_gamma_cast (node, drawable);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Sobel"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_solid_noise {
+    $blurb = 'Create a random cloud-like texture';
+
+    $help = <<'HELP';
+Generates 2D textures using Perlin's classic solid noise function.
+HELP
+
+    &std_pdb_compat('gegl:noise-solid');
+    $date = '2014';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+        { name => 'tileable', type => 'boolean',
+          desc => 'Create a tileable output' },
+        { name => 'turbulent', type => 'boolean',
+          desc => 'Make a turbulent noise' },
+        { name => 'seed', type => 'int32',
+          desc => 'Random seed' },
+        { name => 'detail', type => '0 <= int32 <= 15',
+          desc => 'Detail level' },
+        { name => 'xsize', type => 'float',
+          desc => 'Horizontal texture size' },
+        { name => 'ysize', type => 'float',
+          desc => 'Vertical texture size' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node;
+      gint      x, y, width, height;
+
+      gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height);
+
+      node = gegl_node_new_child (NULL,
+                                  "operation", "gegl:noise-solid",
+                                  "x-size",    xsize,
+                                  "y-size",    ysize,
+                                  "detail",    detail,
+                                  "tileable",  tileable,
+                                  "turbulent", turbulent,
+                                  "seed",      seed,
+                                  "width",     width,
+                                  "height",    height,
+                                  NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Solid Noise"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_spread {
+    $blurb = 'Move pixels around randomly';
+
+    $help = <<'HELP';
+Spreads the pixels of the specified drawable.  Pixels are randomly
+moved to another location whose distance varies from the original by
+the horizontal and vertical spread amounts.
+HELP
+
+    &std_pdb_compat('gegl:noise-spread');
+    $date = '2013';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'spread_amount_x', type => '0 <= float <= 200',
+         desc => 'Horizontal spread amount' },
+       { name => 'spread_amount_y', type => '0 <= float <= 200',
+         desc => 'Vertical spread amount' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node =
+        gegl_node_new_child (NULL,
+                             "operation", "gegl:noise-spread",
+                             "amount-x",  (gint) spread_amount_x,
+                             "amount-y",  (gint) spread_amount_y,
+                             "seed",      (guint) g_random_int (),
+                             NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Spread"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_threshold_alpha {
+    $blurb = 'Make transparency all-or-nothing';
+
+    $help = <<'HELP';
+Make transparency all-or-nothing.
+HELP
+
+    &std_pdb_misc;
+    $date = '1997';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'threshold', type => '0 <= int32 <= 255',
+         desc => 'Threshold' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error) &&
+      gimp_drawable_has_alpha (drawable))
+    {
+      GeglNode *node =
+        gegl_node_new_child (NULL,
+                             "operation", "gimp:threshold-alpha",
+                             "value",     threshold / 255.0,
+                             NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Threshold Alpha"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_video {
+    $blurb = 'Simulate distortion produced by a fuzzy or low-res monitor';
+
+    $help = <<'HELP';
+This function simulates the degradation of being on an old
+low-dotpitch RGB video monitor to the specified drawable.
+HELP
+
+    &std_pdb_compat('gegl:video-degradation');
+    $date = '2014';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'pattern_number', type => '0 <= int32 <= 8',
+         desc => 'Type of RGB pattern to use' },
+       { name => 'additive', type => 'boolean',
+         desc => 'Whether the function adds the result to the original image' },
+       { name => 'rotated', type => 'boolean',
+         desc => 'Whether to rotate the RGB pattern by ninety degrees' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node =
+        gegl_node_new_child (NULL,
+                             "operation", "gegl:video-degradation",
+                             "pattern",   pattern_number,
+                             "additive",  additive,
+                             "rotated",   rotated,
+                             NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Video"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_vinvert {
+    $blurb = 'Invert the brightness of each pixel';
+
+    $help = <<'HELP';
+This function takes an indexed/RGB image and inverts its 'value' in
+HSV space.  The upshot of this is that the color and saturation at any
+given point remains the same, but its brightness is effectively
+inverted.  Quite strange.  Sometimes produces unpleasant color
+artifacts on images from lossy sources (ie. JPEG).
+HELP
+
+    &std_pdb_misc;
+    $date = '1997';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node =
+        gegl_node_new_child (NULL,
+                             "operation", "gegl:value-invert",
+                             NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Value Invert"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_vpropagate {
+    $blurb = 'Propagate certain colors to neighboring pixels',
+
+    $help = <<'HELP';
+Propagate values of the layer.
+HELP
+
+    &std_pdb_compat('gegl:value-propagate');
+    $date = '2015';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'propagate_mode', type => '0 <= int32 <= 7',
+         desc => 'Propagate mode { 0:white, 1:black, 2:middle value 3:foreground to peak, 4:foreground, 
5:background, 6:opaque, 7:transparent }' },
+       { name => 'propagating_channel', type => 'int32',
+         desc => 'Channels which values are propagated' },
+       { name => 'propagating_rate', type => '0.0 <= float <= 1.0',
+         desc => 'Propagating rate' },
+       { name => 'direction_mask', type => '0 <= int32 <= 15',
+         desc => 'Direction mask' },
+       { name => 'lower_limit', type => '0 <= int32 <= 255',
+         desc => 'Lower limit' },
+       { name => 'upper_limit', type => '0 <= int32 <= 255',
+         desc => 'Upper limit' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode  *node;
+      GimpRGB    color;
+      GeglColor *gegl_color = NULL;
+      gint       gegl_mode  = 0;
+      gboolean   to_left    = (direction_mask & (0x1 << 0)) != 0;
+      gboolean   to_top     = (direction_mask & (0x1 << 1)) != 0;
+      gboolean   to_right   = (direction_mask & (0x1 << 2)) != 0;
+      gboolean   to_bottom  = (direction_mask & (0x1 << 3)) != 0;
+      gboolean   value      = (propagating_channel & (0x1 << 0)) != 0;
+      gboolean   alpha      = (propagating_channel & (0x1 << 1)) != 0;
+
+      switch (propagate_mode)
+        {
+        case 0:
+        case 1:
+        case 2:
+          gegl_mode = propagate_mode;
+          break;
+
+        case 3:
+          gegl_mode = propagate_mode;
+          /* fall thru */
+
+        case 4:
+        case 5:
+          gegl_mode = 4;
+
+          if (propagate_mode != 3)
+            gimp_context_get_foreground (context, &color);
+          else
+            gimp_context_get_background (context, &color);
+
+          gegl_color = gimp_gegl_color_new (&color);
+          break;
+
+        case 6:
+        case 7:
+          gegl_mode = propagate_mode - 1;
+          break;
+        }
+
+      node =
+        gegl_node_new_child (NULL,
+                             "operation",        "gegl:value-propagate",
+                             "mode",             gegl_mode,
+                             "lower-threshold",  (gdouble) lower_limit / 255.0,
+                             "upper-threshold",  (gdouble) upper_limit / 255.0,
+                             "rate",             propagating_rate,
+                             "color",            gegl_color,
+                             "top",              to_top,
+                             "left",             to_left,
+                             "right",            to_right,
+                             "bottom",           to_bottom,
+                             "value",            value,
+                             "alpha",            alpha,
+                             NULL);
+
+      if (gegl_color)
+        g_object_unref (gegl_color);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Value Propagate"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_dilate {
+    $blurb = 'Grow lighter areas of the image',
+
+    $help = <<'HELP';
+Dilate image.
+HELP
+
+    &std_pdb_compat('gegl:value-propagate');
+    $date = '2015';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'propagate_mode', type => '0 <= int32 <= 7', dead => 1,
+         desc => 'Propagate mode { 0:white, 1:black, 2:middle value 3:foreground to peak, 4:foreground, 
5:background, 6:opaque, 7:transparent }' },
+       { name => 'propagating_channel', type => 'int32', dead => 1,
+         desc => 'Channels which values are propagated' },
+       { name => 'propagating_rate', type => '0.0 <= float <= 1.0', dead => 1,
+         desc => 'Propagating rate' },
+       { name => 'direction_mask', type => '0 <= int32 <= 15', dead => 1,
+         desc => 'Direction mask' },
+       { name => 'lower_limit', type => '0 <= int32 <= 255', dead => 1,
+         desc => 'Lower limit' },
+       { name => 'upper_limit', type => '0 <= int32 <= 255', dead => 1,
+         desc => 'Upper limit' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node =
+        gegl_node_new_child (NULL,
+                             "operation",       "gegl:value-propagate",
+                             "mode",            0, /* GEGL_VALUE_PROPAGATE_MODE_WHITE */
+                             "lower-threshold", 0.0,
+                             "upper-threshold", 1.0,
+                             "rate",            1.0,
+                             "top",             TRUE,
+                             "left",            TRUE,
+                             "right",           TRUE,
+                             "bottom",          TRUE,
+                             "value",           TRUE,
+                             "alpha",           FALSE,
+                             NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Dilate"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_erode {
+    $blurb = 'Shrink lighter areas of the image',
+
+    $help = <<'HELP';
+Erode image.
+HELP
+
+    &std_pdb_compat('gegl:value-propagate');
+    $date = '2015';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'propagate_mode', type => '0 <= int32 <= 7', dead => 1,
+         desc => 'Propagate mode { 0:white, 1:black, 2:middle value 3:foreground to peak, 4:foreground, 
5:background, 6:opaque, 7:transparent }' },
+       { name => 'propagating_channel', type => 'int32', dead => 1,
+         desc => 'Channels which values are propagated' },
+       { name => 'propagating_rate', type => '0.0 <= float <= 1.0', dead => 1,
+         desc => 'Propagating rate' },
+       { name => 'direction_mask', type => '0 <= int32 <= 15', dead => 1,
+         desc => 'Direction mask' },
+       { name => 'lower_limit', type => '0 <= int32 <= 255', dead => 1,
+         desc => 'Lower limit' },
+       { name => 'upper_limit', type => '0 <= int32 <= 255', dead => 1,
+         desc => 'Upper limit' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node =
+        gegl_node_new_child (NULL,
+                             "operation",       "gegl:value-propagate",
+                             "mode",            1, /* GEGL_VALUE_PROPAGATE_MODE_BLACK */
+                             "lower-threshold", 0.0,
+                             "upper-threshold", 1.0,
+                             "rate",            1.0,
+                             "top",             TRUE,
+                             "left",            TRUE,
+                             "right",           TRUE,
+                             "bottom",          TRUE,
+                             "value",           TRUE,
+                             "alpha",           FALSE,
+                             NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Erode"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_waves {
+    $blurb = 'Distort the image with waves';
+
+    $help = <<'HELP';
+Distort the image with waves.
+HELP
+
+    &std_pdb_compat('gegl:waves');
+    $date = '2013';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'amplitude', type => '0 <= float <= 101',
+         desc => 'The Amplitude of the Waves' },
+       { name => 'phase', type => '-360 <= float <= 360',
+         desc => 'The Phase of the Waves' },
+       { name => 'wavelength', type => '0.1 <= float <= 50',
+         desc => 'The Wavelength of the Waves' },
+       { name => 'type', type => 'boolean',
+         desc => 'Type of waves: { 0 = smeared, 1 = black }' },
+       { name => 'reflective', type => 'boolean', dead => 1,
+         desc => 'Use Reflection (not implemented)' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node;
+      gdouble   width  = gimp_item_get_width  (GIMP_ITEM (drawable));
+      gdouble   height = gimp_item_get_height (GIMP_ITEM (drawable));
+      gdouble   aspect;
+
+      while (phase < 0)
+        phase += 360.0;
+
+      phase = fmod (phase, 360.0);
+
+      aspect = CLAMP (width / height, 0.1, 10.0);
+
+      node = gegl_node_new_child (NULL,
+                                 "operation", "gegl:waves",
+                                 "x",         0.5,
+                                 "y",         0.5,
+                                 "amplitude", amplitude,
+                                 "phi",       (phase - 180.0) / 180.0,
+                                 "period",    wavelength * 2.0,
+                                 "aspect",    aspect,
+                                 "clamp",     ! type,
+                                 NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Waves"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_whirl_pinch {
+    $blurb = 'Distort an image by whirling and pinching';
+
+    $help = <<'HELP';
+Distorts the image by whirling and pinching, which are two common
+center-based, circular distortions.  Whirling is like projecting the
+image onto the surface of water in a toilet and flushing.  Pinching is
+similar to projecting the image onto an elastic surface and pressing
+or pulling on the center of the surface.
+HELP
+
+    &std_pdb_compat('gegl:whirl-pinch');
+    $date = '2013';
+
+    @inargs = (
+       { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+         desc => 'The run mode' },
+       { name => 'image', type => 'image', dead => 1,
+         desc => 'Input image (unused)' },
+       { name => 'drawable', type => 'drawable',
+         desc => 'Input drawable' },
+       { name => 'whirl', type => '-720 <= float <= 720',
+         desc => 'Whirl angle (degrees)' },
+       { name => 'pinch', type => '-1 <= float <= 1',
+         desc => 'Pinch amount' },
+       { name => 'radius', type => '0 <= float <= 2',
+         desc => 'Radius (1.0 is the largest circle that fits in the image, and 2.0 goes all the way to the 
corners)' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node =
+        gegl_node_new_child (NULL,
+                             "operation", "gegl:whirl-pinch",
+                             "whirl",     whirl,
+                             "pinch",     pinch,
+                             "radius",    radius,
+                             NULL);
+
+      node = wrap_in_selection_bounds (node, drawable);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Whirl and Pinch"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub plug_in_wind {
+    $blurb = 'Smear image to give windblown effect';
+
+    $help = <<'HELP';
+Renders a wind effect.
+HELP
+
+    &std_pdb_compat('gegl:wind');
+    $date = '2015';
+
+    @inargs = (
+  { name => 'run_mode', type => 'enum GimpRunMode', dead => 1,
+    desc => 'The run mode' },
+  { name => 'image', type => 'image', dead => 1,
+    desc => 'Input image (unused)' },
+  { name => 'drawable', type => 'drawable',
+    desc => 'Input drawable' },
+  { name => 'threshold', type => '0 <= int32 <= 50',
+    desc => 'Controls where blending will be done' },
+  { name => 'direction', type => '0 <= int32 <= 3',
+    desc => 'Wind direction { 0:left, 1:right, 2:top, 3:bottom }' },
+  { name => 'strength', type => '1 <= int32 <= 100',
+    desc => 'Controls the extent of the blending' },
+  { name => 'algorithm', type => '0 <= int32 <= 1',
+    desc => 'Algorithm { WIND (0), BLAST (1) }' },
+  { name => 'edge', type => '0 <= int32 <= 2',
+    desc => 'Affected edge { BOTH (0), LEADING (1), TRAILING (2) }' },
+    );
+
+    %invoke = (
+  code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node =
+        gegl_node_new_child (NULL,
+                             "operation", "gegl:wind",
+                             "threshold", threshold,
+                             "direction", direction,
+                             "strength",  strength,
+                             "style",     algorithm,
+                             "edge",      edge,
+                             NULL);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Wind"),
+                                     node);
+      g_object_unref (node);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+$extra{app}->{code} = <<'CODE';
+static GeglNode *
+wrap_in_selection_bounds (GeglNode     *node,
+                          GimpDrawable *drawable)
+{
+  gint x, y;
+  gint width, height;
+
+  if (gimp_item_mask_intersect (GIMP_ITEM (drawable),
+                                &x, &y, &width, &height))
+    {
+      GeglNode *new_node;
+      GeglNode *input;
+      GeglNode *output;
+      GeglNode *translate_before;
+      GeglNode *crop;
+      GeglNode *translate_after;
+
+      new_node = gegl_node_new ();
+
+      gegl_node_add_child (new_node, node);
+      g_object_unref (node);
+
+      input  = gegl_node_get_input_proxy  (new_node, "input");
+      output = gegl_node_get_output_proxy (new_node, "output");
+
+      translate_before = gegl_node_new_child (new_node,
+                                              "operation", "gegl:translate",
+                                              "x",         (gdouble) -x,
+                                              "y",         (gdouble) -y,
+                                              NULL);
+      crop = gegl_node_new_child (new_node,
+                                  "operation", "gegl:crop",
+                                  "width",     (gdouble) width,
+                                  "height",    (gdouble) height,
+                                  NULL);
+      translate_after = gegl_node_new_child (new_node,
+                                             "operation", "gegl:translate",
+                                             "x",         (gdouble) x,
+                                             "y",         (gdouble) y,
+                                             NULL);
+
+      gegl_node_link_many (input,
+                           translate_before,
+                           crop,
+                           node,
+                           translate_after,
+                           output,
+                           NULL);
+
+      return new_node;
+    }
+  else
+    {
+      return node;
+    }
+}
+
+static GeglNode *
+wrap_in_gamma_cast (GeglNode     *node,
+                    GimpDrawable *drawable)
+{
+  if (! gimp_drawable_get_linear (drawable))
+    {
+      const Babl *drawable_format;
+      const Babl *cast_format;
+      GeglNode   *new_node;
+      GeglNode   *input;
+      GeglNode   *output;
+      GeglNode   *cast_before;
+      GeglNode   *cast_after;
+
+      drawable_format = gimp_drawable_get_format (drawable);
+
+      cast_format =
+        gimp_babl_format (gimp_babl_format_get_base_type (drawable_format),
+                          gimp_babl_precision (gimp_babl_format_get_component_type (drawable_format),
+                                               TRUE),
+                          babl_format_has_alpha (drawable_format));
+
+      new_node = gegl_node_new ();
+
+      gegl_node_add_child (new_node, node);
+      g_object_unref (node);
+
+      input  = gegl_node_get_input_proxy  (new_node, "input");
+      output = gegl_node_get_output_proxy (new_node, "output");
+
+      cast_before = gegl_node_new_child (new_node,
+                                         "operation",     "gegl:cast-format",
+                                         "input-format",  drawable_format,
+                                         "output-format", cast_format,
+                                         NULL);
+      cast_after  = gegl_node_new_child (new_node,
+                                         "operation",     "gegl:cast-format",
+                                         "input-format",  cast_format,
+                                         "output-format", drawable_format,
+                                         NULL);
+
+      gegl_node_link_many (input,
+                           cast_before,
+                           node,
+                           cast_after,
+                           output,
+                           NULL);
+
+      return new_node;
+    }
+  else
+    {
+      return node;
+    }
+}
+
+static GeglNode *
+create_buffer_source_node (GeglNode     *parent,
+                           GimpDrawable *drawable)
+{
+  GeglNode   *new_node;
+  GeglBuffer *buffer;
+
+  buffer = gimp_drawable_get_buffer (drawable);
+  g_object_ref (buffer);
+  new_node = gegl_node_new_child (parent,
+                                  "operation", "gegl:buffer-source",
+                                  "buffer", buffer,
+                                  NULL);
+  g_object_unref (buffer);
+  return new_node;
+}
+
+static gboolean
+bump_map (GimpDrawable *drawable,
+          GimpDrawable *bump_map,
+          gdouble       azimuth,
+          gdouble       elevation,
+          gint          depth,
+          gint          offset_x,
+          gint          offset_y,
+          gdouble       waterlevel,
+          gdouble       ambient,
+          gboolean      compensate,
+          gboolean      invert,
+          gint          type,
+          gboolean      tiled,
+          GimpProgress  *progress,
+          GError       **error)
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *gegl;
+      GeglNode *node;
+      GeglNode *src_node;
+
+      gegl = gegl_node_new ();
+
+      node = gegl_node_new_child (gegl,
+                                  "operation", "gegl:bump-map",
+                                  "tiled",      tiled,
+                                  "type",       type,
+                                  "compensate", compensate,
+                                  "invert",     invert,
+                                  "azimuth",    azimuth,
+                                  "elevation",  elevation,
+                                  "depth",      depth,
+                                  "offset_x",   offset_x,
+                                  "offset_y",   offset_y,
+                                  "waterlevel", waterlevel,
+                                  "ambient",    ambient,
+                                  NULL);
+
+      src_node = create_buffer_source_node (gegl, bump_map);
+
+      gegl_node_connect_to (src_node, "output", node, "aux");
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Bump Map"),
+                                     node);
+      g_object_unref (gegl);
+
+      return TRUE;
+    }
+    else
+      return FALSE;
+}
+
+static gboolean
+displace (GimpDrawable  *drawable,
+          gdouble        amount_x,
+          gdouble        amount_y,
+          gboolean       do_x,
+          gboolean       do_y,
+          GimpDrawable  *displace_map_x,
+          GimpDrawable  *displace_map_y,
+          gint           displace_type,
+          gint           displace_mode,
+          GimpProgress  *progress,
+          GError       **error)
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      if (do_x || do_y)
+        {
+          GeglNode *gegl;
+          GeglNode *node;
+          GeglAbyssPolicy abyss_policy = GEGL_ABYSS_NONE;
+
+          switch (displace_type)
+            {
+              case 1:
+                abyss_policy = GEGL_ABYSS_LOOP;
+                break;
+              case 2:
+                abyss_policy = GEGL_ABYSS_CLAMP;
+                break;
+              case 3:
+                abyss_policy = GEGL_ABYSS_BLACK;
+                break;
+            }
+
+          gegl = gegl_node_new ();
+
+          node = gegl_node_new_child (gegl,
+                                      "operation",     "gegl:displace",
+                                      "displace_mode", displace_mode,
+                                      "sampler_type",  GEGL_SAMPLER_CUBIC,
+                                      "abyss_policy",  abyss_policy,
+                                      "amount_x",      amount_x,
+                                      "amount_y",      amount_y,
+                                      NULL);
+
+          if (do_x)
+            {
+              GeglNode *src_node;
+              src_node = create_buffer_source_node (gegl, displace_map_x);
+              gegl_node_connect_to (src_node, "output", node, "aux");
+            }
+
+          if (do_y)
+            {
+              GeglNode *src_node;
+              src_node = create_buffer_source_node (gegl, displace_map_y);
+              gegl_node_connect_to (src_node, "output", node, "aux2");
+            }
+
+          gimp_drawable_apply_operation (drawable, progress,
+                                         C_("undo-type", "Displace"),
+                                         node);
+          g_object_unref (gegl);
+        }
+
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+static gboolean
+gaussian_blur (GimpDrawable  *drawable,
+               gdouble        horizontal,
+               gdouble        vertical,
+               GimpProgress  *progress,
+               GError       **error)
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GeglNode *node;
+
+      node = gegl_node_new_child (NULL,
+                                  "operation",    "gegl:gaussian-blur",
+                                  "std-dev-x",    horizontal * 0.32,
+                                  "std-dev-y",    vertical   * 0.32,
+                                  "abyss-policy", 1,
+                                  NULL);
+
+      node = wrap_in_gamma_cast (node, drawable);
+
+      gimp_drawable_apply_operation (drawable, progress,
+                                     C_("undo-type", "Gaussian Blur"),
+                                     node);
+      g_object_unref (node);
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+CODE
+
+@headers = qw("libgimpbase/gimpbase.h"
+              "libgimpconfig/gimpconfig.h"
+              "libgimpmath/gimpmath.h"
+              "gegl/gimp-babl.h"
+              "gegl/gimp-gegl-utils.h"
+              "config/gimpcoreconfig.h"
+              "core/gimp.h"
+              "core/gimpchannel.h"
+              "core/gimpcontext.h"
+              "core/gimpdrawable-operation.h"
+              "core/gimpimage-color-profile.h"
+              "core/gimpimage-crop.h"
+              "core/gimpimage-resize.h"
+              "core/gimpimage-rotate.h"
+              "core/gimpimage-undo.h"
+              "core/gimppickable.h"
+              "core/gimppickable-auto-shrink.h"
+              "gimppdberror.h"
+              "gimppdb-utils.h"
+              "gimp-intl.h");
+
+@procs = qw(plug_in_alienmap2
+            plug_in_antialias
+            plug_in_apply_canvas
+            plug_in_applylens
+            plug_in_autocrop
+            plug_in_autocrop_layer
+            plug_in_autostretch_hsv
+            plug_in_bump_map
+            plug_in_bump_map_tiled
+            plug_in_c_astretch
+            plug_in_colors_channel_mixer
+            plug_in_colortoalpha
+            plug_in_convmatrix
+            plug_in_cubism
+            plug_in_deinterlace
+            plug_in_diffraction
+            plug_in_displace
+            plug_in_displace_polar
+            plug_in_edge
+            plug_in_engrave
+            plug_in_exchange
+            plug_in_flarefx
+            plug_in_gauss
+            plug_in_gauss_iir
+            plug_in_gauss_iir2
+            plug_in_gauss_rle
+            plug_in_gauss_rle2
+            plug_in_glasstile
+            plug_in_hsv_noise
+            plug_in_icc_profile_info
+            plug_in_icc_profile_file_info
+            plug_in_icc_profile_apply
+            plug_in_icc_profile_apply_rgb
+            plug_in_icc_profile_set
+            plug_in_icc_profile_set_rgb
+            plug_in_illusion
+            plug_in_laplace
+            plug_in_lens_distortion
+            plug_in_make_seamless
+            plug_in_maze
+            plug_in_mblur
+            plug_in_mblur_inward
+            plug_in_mosaic
+            plug_in_nova
+            plug_in_papertile
+            plug_in_pixelize
+            plug_in_pixelize2
+            plug_in_plasma
+            plug_in_polar_coords
+            plug_in_red_eye_removal
+            plug_in_randomize_hurl
+            plug_in_randomize_pick
+            plug_in_randomize_slur
+            plug_in_rgb_noise
+            plug_in_rotate
+            plug_in_noisify
+            plug_in_sel_gauss
+            plug_in_semiflatten
+            plug_in_shift
+            plug_in_sinus
+            plug_in_sobel
+            plug_in_solid_noise
+            plug_in_spread
+            plug_in_threshold_alpha
+            plug_in_video
+            plug_in_vinvert
+            plug_in_vpropagate
+            plug_in_dilate
+            plug_in_erode
+            plug_in_waves
+            plug_in_whirl_pinch
+            plug_in_wind);
+
+%exports = (app => [@procs], lib => []);
+
+$desc = 'Plug-in Compat';
+$doc_title = 'gimpplugincompat';
+$doc_short_desc = 'Compatibility for removed plug-ins.';
+$doc_long_desc = 'Functions that perform the operation of removed plug-ins using GEGL operations or other 
GIMP internal functions.';
+
+1;
diff --git a/pdb/groups/procedural_db.pdb b/pdb/groups/procedural_db.pdb
new file mode 100644
index 0000000..05856ef
--- /dev/null
+++ b/pdb/groups/procedural_db.pdb
@@ -0,0 +1,521 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub procedural_db_temp_name {
+    $blurb = 'Generates a unique temporary PDB name.';
+
+    $help = <<'HELP';
+This procedure generates a temporary PDB entry name that is guaranteed to be
+unique.
+HELP
+
+    &andy_pdb_misc('1998');
+
+    @outargs = (
+       { name => 'temp_name', type => 'string',
+         desc => 'A unique temporary name for a temporary PDB entry' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  static gint proc_number = 0;
+
+  temp_name = g_strdup_printf ("temp-procedure-number-%d", proc_number++);
+}
+CODE
+    );
+}
+
+sub procedural_db_dump {
+    $blurb = 'Dumps the current contents of the procedural database';
+
+    $help = <<'HELP';
+This procedure dumps the contents of the procedural database to the specified
+file. The file will contain all of the information provided for each registered
+procedure.
+HELP
+
+    &std_pdb_misc;
+    $author = 'Spencer Kimball & Josh MacDonald';
+    $copyright = $author . ' & Peter Mattis';
+
+    @inargs = (
+       { name => 'filename', type => 'string', allow_non_utf8 => 1,
+          non_empty => 1,
+         desc => 'The dump filename' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GFile *file = g_file_new_for_path (filename);
+
+  success = gimp_pdb_dump (gimp->pdb, file, error);
+
+  g_object_unref (file);
+}
+CODE
+    );
+}
+
+sub procedural_db_query {
+    $blurb = <<'BLURB';
+Queries the procedural database for its contents using regular expression
+matching.
+BLURB
+
+    $help = <<'HELP';
+This procedure queries the contents of the procedural database. It is supplied
+with seven arguments matching procedures on { name, blurb, help, author,
+copyright, date, procedure type}. This is accomplished using regular expression
+matching. For instance, to find all procedures with "jpeg" listed in the blurb,
+all seven arguments can be supplied as ".*", except for the second, which can
+be supplied as ".*jpeg.*". There are two return arguments for this procedure.
+The first is the number of procedures matching the query. The second is a
+concatenated list of procedure names corresponding to those matching the query.
+If no matching entries are found, then the returned string is NULL and the
+number of entries is 0.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'name', type => 'string', allow_non_utf8 => 1,
+         desc => 'The regex for procedure name' },
+       { name => 'blurb', type => 'string', allow_non_utf8 => 1,
+         desc => 'The regex for procedure blurb' },
+       { name => 'help', type => 'string', allow_non_utf8 => 1,
+         desc => 'The regex for procedure help' },
+       { name => 'author', type => 'string', allow_non_utf8 => 1,
+         desc => 'The regex for procedure author' },
+       { name => 'copyright', type => 'string', allow_non_utf8 => 1,
+         desc => 'The regex for procedure copyright' },
+       { name => 'date', type => 'string', allow_non_utf8 => 1,
+         desc => 'The regex for procedure date' },
+       { name => 'proc_type', type => 'string', allow_non_utf8 => 1,
+         desc => 'The regex for procedure type: { \'Internal GIMP procedure\',
+                  \'GIMP Plug-in\', \'GIMP Extension\',
+                  \'Temporary Procedure\' }' }
+    );
+
+    @outargs = (
+       { name  => 'procedure_names', type  => 'stringarray', void_ret => 1,
+         desc  => 'The list of procedure names',
+         array => { name  => 'num_matches',
+                    desc  => 'The number of matching procedures' } }
+    );
+
+    %invoke = (
+       code => <<CODE
+{
+  success = gimp_pdb_query (gimp->pdb,
+                            name, blurb, help, author,
+                            copyright, date, proc_type,
+                            &num_matches, &procedure_names,
+                            error);
+}
+CODE
+    );
+}
+
+sub procedural_db_proc_exists {
+    $blurb = <<'BLURB';
+Checks if the specified procedure exists in the procedural database
+BLURB
+
+    $help = <<'HELP';
+This procedure checks if the specified procedure is registered in the
+procedural database.
+HELP
+
+    &neo_pdb_misc('2008', '2.6');
+
+    @inargs = (
+       { name  => 'procedure_name', type  => 'string', non_empty => 1,
+         desc  => 'The procedure name' }
+    );
+
+    @outargs = (
+       { name => 'exists', type => 'boolean',
+         desc => 'Whether a procedure of that name is registered' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpProcedure *procedure;
+  gchar         *canonical;
+
+  canonical = gimp_canonicalize_identifier (procedure_name);
+
+  procedure = gimp_pdb_lookup_procedure (gimp->pdb, canonical);
+
+  if (! procedure)
+    {
+      procedure_name = gimp_pdb_lookup_compat_proc_name (gimp->pdb, canonical);
+
+      if (procedure_name)
+        procedure = gimp_pdb_lookup_procedure (gimp->pdb, procedure_name);
+    }
+
+  g_free (canonical);
+
+  exists = (procedure != NULL);
+}
+CODE
+    );
+}
+
+sub procedural_db_proc_info {
+    $blurb = <<'BLURB';
+Queries the procedural database for information on the specified procedure.
+BLURB
+
+    $help = <<'HELP';
+This procedure returns information on the specified procedure. A short
+blurb, detailed help, author(s), copyright information, procedure
+type, number of input, and number of return values are returned. For
+specific information on each input argument and return value, use the
+gimp_procedural_db_proc_arg() and gimp_procedural_db_proc_val()
+procedures.
+HELP
+
+    &std_pdb_misc;
+    $date = '1997';
+
+    @inargs = (
+       { name  => 'procedure_name', type  => 'string', non_empty => 1,
+         desc  => 'The procedure name' }
+    );
+
+    @outargs = (
+       { name => 'blurb', type => 'string', void_ret => 1, wrap => 1,
+         desc => 'A short blurb' },
+       { name => 'help', type => 'string',
+         desc => 'Detailed procedure help' },
+       { name => 'author', type => 'string',
+         desc => 'Author(s) of the procedure' },
+       { name => 'copyright', type => 'string',
+         desc => 'The copyright' },
+       { name => 'date', type => 'string',
+         desc => 'Copyright date' },
+        { name => 'proc_type', type => 'enum GimpPDBProcType',
+         desc => 'The procedure type' },
+        { name => 'num_args', type => 'int32',
+         desc => 'The number of input arguments' },
+        { name => 'num_values', type => 'int32',
+         desc => 'The number of return values' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPDBProcType  ptype;
+  gchar           *canonical;
+
+  canonical = gimp_canonicalize_identifier (procedure_name);
+
+  success = gimp_pdb_proc_info (gimp->pdb, canonical,
+                                &blurb, &help, &author,
+                                &copyright, &date, &ptype,
+                                &num_args, &num_values,
+                                error);
+  proc_type = ptype;
+
+  g_free (canonical);
+}
+CODE
+    );
+}
+
+sub procedural_db_proc_arg {
+    $blurb = <<BLURB;
+Queries the procedural database for information on the specified procedure's
+argument.
+BLURB
+
+    $help = <<HELP;
+This procedure returns information on the specified procedure's argument. The
+argument type, name, and a description are retrieved.
+HELP
+
+    &std_pdb_misc;
+    $date = '1997';
+
+    @inargs = (
+       { name => 'procedure_name', type  => 'string', non_empty => 1,
+         desc => 'The procedure name' },
+       { name => 'arg_num', type => 'int32',
+         desc => 'The argument number' }
+    );
+
+    @outargs = (
+       { name => 'arg_type', type => 'enum GimpPDBArgType (no GIMP_PDB_END)',
+         void_ret => 1,
+         desc => "The type of argument" },
+       { name => 'arg_name', type => 'string',
+         desc => 'The name of the argument' },
+       { name => 'arg_desc', type => 'string',
+         desc => 'A description of the argument' }
+    );
+
+   %invoke = (
+       code => <<CODE
+{
+  GimpProcedure *proc;
+  gchar         *canonical;
+
+  canonical = gimp_canonicalize_identifier (procedure_name);
+
+  proc = gimp_pdb_lookup_procedure (gimp->pdb, canonical);
+
+  if (! proc)
+    {
+      const gchar *compat_name;
+
+      compat_name = gimp_pdb_lookup_compat_proc_name (gimp->pdb, canonical);
+
+      if (compat_name)
+        proc = gimp_pdb_lookup_procedure (gimp->pdb, compat_name);
+    }
+
+  g_free (canonical);
+
+  if (proc && (arg_num >= 0 && arg_num < proc->num_args))
+    {
+      GParamSpec *pspec = proc->args[arg_num];
+
+      arg_type = gimp_pdb_compat_arg_type_from_gtype (G_PARAM_SPEC_VALUE_TYPE (pspec));
+      arg_name = g_strdup (g_param_spec_get_name (pspec));
+      arg_desc = gimp_param_spec_get_desc (pspec);
+    }
+  else
+    success = FALSE;
+}
+CODE
+   );
+}
+
+sub procedural_db_proc_val {
+    $blurb = <<BLURB;
+Queries the procedural database for information on the specified procedure's
+return value.
+BLURB
+
+    $help = <<HELP;
+This procedure returns information on the specified procedure's return value.
+The return value type, name, and a description are retrieved.
+HELP
+
+    &std_pdb_misc;
+    $date = '1997';
+
+    @inargs = (
+       { name => 'procedure_name', type  => 'string', non_empty => 1,
+         desc => 'The procedure name' },
+       { name => 'val_num', type => 'int32',
+         desc => 'The return value number' }
+    );
+
+    @outargs = (
+       { name => 'val_type', type => 'enum GimpPDBArgType (no GIMP_PDB_END)',
+         void_ret => 1,
+         desc => "The type of return value" },
+       { name => 'val_name', type => 'string',
+         desc => 'The name of the return value' },
+       { name => 'val_desc', type => 'string',
+         desc => 'A description of the return value' }
+    );
+
+   %invoke = (
+       code => <<CODE
+{
+  GimpProcedure *proc;
+  gchar         *canonical;
+
+  canonical = gimp_canonicalize_identifier (procedure_name);
+
+  proc = gimp_pdb_lookup_procedure (gimp->pdb, canonical);
+
+  if (! proc)
+    {
+      const gchar *compat_name;
+
+      compat_name = gimp_pdb_lookup_compat_proc_name (gimp->pdb, canonical);
+
+      if (compat_name)
+        proc = gimp_pdb_lookup_procedure (gimp->pdb, compat_name);
+    }
+
+  g_free (canonical);
+
+  if (proc && (val_num >= 0 && val_num < proc->num_values))
+    {
+      GParamSpec *pspec = proc->values[val_num];
+
+      val_type = gimp_pdb_compat_arg_type_from_gtype (G_PARAM_SPEC_VALUE_TYPE (pspec));
+      val_name = g_strdup (g_param_spec_get_name (pspec));
+      val_desc = gimp_param_spec_get_desc (pspec);
+    }
+  else
+    success = FALSE;
+}
+CODE
+   );
+}
+
+sub procedural_db_get_data {
+    $blurb = 'Returns data associated with the specified identifier.';
+
+    $help = <<'HELP';
+This procedure returns any data which may have been associated with the
+specified identifier. The data is a variable length array of bytes. If no data
+has been associated with the identifier, an error is returned.
+HELP
+
+    &std_pdb_misc;
+    $date = '1997';
+
+    @inargs = (
+       { name => 'identifier', type => 'string', non_empty => 1,
+         desc => 'The identifier associated with data' }
+    );
+
+    @outargs = (
+       { name  => 'data', type  => 'int8array', wrap => 1, void_ret => 1,
+         desc  => 'A byte array containing data',
+         array => { name => 'bytes', type => '1 <= int32',
+                    desc => 'The number of bytes in the data' } }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gchar        *canonical = gimp_canonicalize_identifier (identifier);
+  const guint8 *orig_data;
+
+  orig_data = gimp_plug_in_manager_get_data (gimp->plug_in_manager,
+                                             canonical, &bytes);
+
+  g_free (canonical);
+
+  if (orig_data)
+    data = g_memdup (orig_data, bytes);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub procedural_db_get_data_size {
+    $blurb = 'Returns size of data associated with the specified identifier.';
+
+    $help = <<'HELP';
+This procedure returns the size of any data which may have been associated with
+the specified identifier. If no data has been associated with the identifier,
+an error is returned.
+HELP
+
+    &nick_pdb_misc('1998');
+
+    @inargs = (
+       { name => 'identifier', type => 'string', non_empty => 1,
+         desc => 'The identifier associated with data' }
+    );
+
+    @outargs = (
+       { name => 'bytes', type => '1 <= int32',
+         desc => 'The number of bytes in the data' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gchar *canonical = gimp_canonicalize_identifier (identifier);
+
+  if (! gimp_plug_in_manager_get_data (gimp->plug_in_manager,
+                                       canonical, &bytes))
+    success = FALSE;
+
+  g_free (canonical);
+}
+CODE
+    );
+}
+
+sub procedural_db_set_data {
+    $blurb = 'Associates the specified identifier with the supplied data.';
+
+    $help = <<'HELP';
+This procedure associates the supplied data with the provided identifier. The
+data may be subsequently retrieved by a call to 'procedural-db-get-data'.
+HELP
+
+    &std_pdb_misc;
+    $date = '1997';
+
+    @inargs = (
+       { name => 'identifier', type => 'string', non_empty => 1,
+         desc => 'The identifier associated with data' },
+       { name  => 'data', type  => 'int8array',
+         desc  => 'A byte array containing data', wrap => 1,
+         array => { name => 'bytes', type => '1 <= int32',
+                    desc => 'The number of bytes in the data' } }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gchar *canonical = gimp_canonicalize_identifier (identifier);
+
+  gimp_plug_in_manager_set_data (gimp->plug_in_manager,
+                                 canonical, bytes, data);
+
+  g_free (canonical);
+}
+CODE
+    );
+}
+
+
+@headers = qw("libgimpbase/gimpbase.h"
+              "core/gimp.h"
+              "core/gimpparamspecs-desc.h"
+              "plug-in/gimppluginmanager-data.h"
+              "gimppdb-query.h"
+              "gimp-pdb-compat.h");
+
+@procs = qw(procedural_db_temp_name
+            procedural_db_dump
+            procedural_db_query
+            procedural_db_proc_exists
+            procedural_db_proc_info
+            procedural_db_proc_arg procedural_db_proc_val
+           procedural_db_get_data procedural_db_get_data_size
+           procedural_db_set_data);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Procedural database';
+$doc_title = 'gimpproceduraldb';
+$doc_short_desc = 'Functions for querying and changing procedural database (PDB) entries.';
+$doc_long_desc = 'Functions for querying and changing procedural database (PDB) entries.';
+
+1;
diff --git a/pdb/groups/progress.pdb b/pdb/groups/progress.pdb
new file mode 100644
index 0000000..24afd4a
--- /dev/null
+++ b/pdb/groups/progress.pdb
@@ -0,0 +1,321 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub progress_init {
+    $blurb = 'Initializes the progress bar for the current plug-in.';
+
+    $help = <<'HELP';
+Initializes the progress bar for the current plug-in. It is only valid to call
+this procedure from a plug-in.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'message', type => 'string', null_ok => 1, wrap => 1,
+         desc => 'Message to use in the progress dialog' },
+       { name => 'gdisplay', type => 'display', none_ok => 1,
+         desc => 'GimpDisplay to update progressbar in, or -1 for a separate
+                  window' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPlugIn *plug_in = gimp->plug_in_manager->current_plug_in;
+
+  if (plug_in && plug_in->open)
+    {
+      if (! gimp->no_interface)
+        gimp_plug_in_progress_start (plug_in, message, gdisplay);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub progress_update {
+    $blurb = 'Updates the progress bar for the current plug-in.';
+
+    $help = <<'HELP';
+Updates the progress bar for the current plug-in. It is only valid to call this
+procedure from a plug-in.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'percentage', type => 'float', wrap => 1,
+         desc => 'Percentage of progress completed which must
+                   be between 0.0 and 1.0' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPlugIn *plug_in = gimp->plug_in_manager->current_plug_in;
+
+  if (plug_in && plug_in->open)
+    {
+      if (! gimp->no_interface)
+       gimp_plug_in_progress_set_value (plug_in, percentage);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub progress_pulse {
+    $blurb = 'Pulses the progress bar for the current plug-in.';
+
+    $help = <<'HELP';
+Updates the progress bar for the current plug-in. It is only valid to
+call this procedure from a plug-in. Use this function instead of
+gimp_progress_update() if you cannot tell how much progress has been
+made. This usually causes the the progress bar to enter "activity
+mode", where a block bounces back and forth.
+HELP
+
+    &neo_pdb_misc('2005', '2.4');
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPlugIn *plug_in = gimp->plug_in_manager->current_plug_in;
+
+  if (plug_in && plug_in->open)
+    {
+      if (! gimp->no_interface)
+        gimp_plug_in_progress_pulse (plug_in);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub progress_set_text {
+    $blurb = 'Changes the text in the progress bar for the current plug-in.';
+
+    $help = <<'HELP';
+This function changes the text in the progress bar for the current
+plug-in. Unlike gimp_progress_init() it does not change the displayed
+value.
+HELP
+
+    &neo_pdb_misc('2005', '2.4');
+
+    @inargs = (
+       { name => 'message', type => 'string', null_ok => 1,
+         desc => 'Message to use in the progress dialog' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPlugIn *plug_in = gimp->plug_in_manager->current_plug_in;
+
+  if (plug_in && plug_in->open)
+    {
+      if (! gimp->no_interface)
+        gimp_plug_in_progress_set_text (plug_in, message);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub progress_end {
+    $blurb = 'Ends the progress bar for the current plug-in.';
+
+    $help = <<'HELP';
+Ends the progress display for the current plug-in. Most plug-ins don't need to call this, they just exit 
when the work is done. It is only valid to call this
+procedure from a plug-in.
+HELP
+
+    &neo_pdb_misc('2007', '2.4');
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPlugIn *plug_in = gimp->plug_in_manager->current_plug_in;
+
+  if (plug_in && plug_in->open)
+    {
+      GimpPlugInProcFrame *proc_frame = gimp_plug_in_get_proc_frame (plug_in);
+
+      gimp_plug_in_progress_end (plug_in, proc_frame);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub progress_get_window_handle {
+    $blurb = 'Returns the native window ID of the toplevel window this plug-in\'s progress is displayed in.';
+
+    $help = <<'HELP';
+This function returns the native window ID of the toplevel window this plug-in\'s progress is displayed in.
+HELP
+
+    &mitch_pdb_misc('2004', '2.2');
+
+    @outargs = (
+       { name => 'window', type => 'int32',
+         desc => 'The progress bar\'s toplevel window' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPlugIn *plug_in = gimp->plug_in_manager->current_plug_in;
+
+  if (plug_in && plug_in->open)
+    {
+      if (! gimp->no_interface)
+        window = gimp_plug_in_progress_get_window_id (plug_in);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub progress_install {
+    $blurb = 'Installs a progress callback for the current plug-in.';
+
+    $help = <<'HELP';
+This function installs a temporary PDB procedure which will handle all
+progress calls made by this plug-in and any procedure it calls. Calling
+this function multiple times simply replaces the old progress callbacks.
+HELP
+
+    &mitch_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'progress_callback', type => 'string', non_empty => 1,
+         desc => 'The callback PDB proc to call',
+          wrap => 1 }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPlugIn *plug_in = gimp->plug_in_manager->current_plug_in;
+
+  if (plug_in && plug_in->open)
+    success = gimp_plug_in_progress_install (plug_in, progress_callback);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub progress_uninstall {
+    $blurb = 'Uninstalls the progress callback for the current plug-in.';
+
+    $help = <<'HELP';
+This function uninstalls any progress callback installed with
+gimp_progress_install() before.
+HELP
+
+    &mitch_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'progress_callback', type => 'string', non_empty => 1,
+         desc => 'The name of the callback registered for this progress',
+          wrap => 1 }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPlugIn *plug_in = gimp->plug_in_manager->current_plug_in;
+
+  if (plug_in && plug_in->open)
+    success = gimp_plug_in_progress_uninstall (plug_in, progress_callback);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub progress_cancel {
+    $blurb = 'Cancels a running progress.';
+
+    $help = <<'HELP';
+This function cancels the currently running progress.
+HELP
+
+    &mitch_pdb_misc('2004', '2.2');
+
+    @inargs = (
+       { name => 'progress_callback', type => 'string', non_empty => 1,
+         desc => 'The name of the callback registered for this progress' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpPlugIn *plug_in = gimp->plug_in_manager->current_plug_in;
+
+  if (plug_in && plug_in->open)
+    success = gimp_plug_in_progress_cancel (plug_in, progress_callback);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+@headers = qw("core/gimp.h"
+              "plug-in/gimpplugin.h"
+              "plug-in/gimpplugin-progress.h"
+              "plug-in/gimppluginmanager.h");
+
+@procs = qw(progress_init
+            progress_update
+            progress_pulse
+            progress_set_text
+            progress_end
+            progress_get_window_handle
+            progress_install
+            progress_uninstall
+            progress_cancel);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Progress';
+$doc_title = 'gimpprogress';
+$doc_short_desc = "Functions for embedding the progress bar into a plug-in's GUI.";
+$doc_long_desc = "Functions for embedding the progress bar into a plug-in's GUI.";
+
+1;
diff --git a/pdb/groups/selection.pdb b/pdb/groups/selection.pdb
new file mode 100644
index 0000000..7622726
--- /dev/null
+++ b/pdb/groups/selection.pdb
@@ -0,0 +1,578 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub selection_bounds {
+    $blurb = 'Find the bounding box of the current selection.';
+
+    $help = <<'HELP';
+This procedure returns whether there is a selection for the specified image. If
+there is one, the upper left and lower right corners of the bounding box are
+returned. These coordinates are relative to the image. Please note that the
+pixel specified by the lower right coordinate of the bounding box is not
+part of the selection. The selection ends at the upper left corner of this
+pixel. This means the width of the selection can be calculated as (x2 - x1),
+its height as (y2 - y1).
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' }
+    );
+
+    @outargs = (
+       { name => 'non_empty', type => 'boolean', void_ret => 1,
+         desc => 'TRUE if there is a selection' },
+       { name => 'x1', type => 'int32',
+         desc => 'x coordinate of upper left corner of selection bounds' },
+       { name => 'y1', type => 'int32',
+         desc => 'y coordinate of upper left corner of selection bounds' },
+       { name => 'x2', type => 'int32',
+         desc => 'x coordinate of lower right corner of selection bounds' },
+       { name => 'y2', type => 'int32',
+         desc => 'y coordinate of lower right corner of selection bounds' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gint x, y, w, h;
+
+  non_empty = gimp_item_bounds (GIMP_ITEM (gimp_image_get_mask (image)),
+                                &x, &y, &w, &h);
+
+  x1 = x;
+  y1 = y;
+  x2 = x + w;
+  y2 = y + h;
+}
+CODE
+    );
+}
+
+sub selection_value {
+    $blurb = 'Find the value of the selection at the specified coordinates.';
+
+    $help = <<'HELP';
+This procedure returns the value of the selection at the specified coordinates.
+If the coordinates lie out of bounds, 0 is returned.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+       { name => 'x', type => 'int32',
+         desc => 'x coordinate of value' },
+       { name => 'y', type => 'int32',
+         desc => 'y coordinate of value' }
+    );
+
+    @outargs = (
+       { name => 'value', type => '0 <= int32 <= 255',
+         desc => 'Value of the selection' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gdouble val;
+
+  val= gimp_pickable_get_opacity_at (GIMP_PICKABLE (gimp_image_get_mask (image)),
+                                     x, y);
+
+  value = ROUND (CLAMP (val, 0.0, 1.0) * 255.0);
+}
+CODE
+    );
+}
+
+sub selection_is_empty {
+    $blurb = 'Determine whether the selection is empty.';
+
+    $help = <<'HELP';
+This procedure returns TRUE if the selection for the specified image is empty.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' }
+    );
+
+    @outargs = (
+       { name => 'is_empty', type => 'boolean',
+         desc => 'Is the selection empty?' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  is_empty = gimp_channel_is_empty (gimp_image_get_mask (image));
+}
+CODE
+    );
+}
+
+sub selection_translate {
+    $blurb = 'Translate the selection by the specified offsets.';
+
+    $help = <<'HELP';
+This procedure actually translates the selection for the specified image by the
+specified offsets. Regions that are translated from beyond the bounds of the
+image are set to empty. Valid regions of the selection which are translated
+beyond the bounds of the image because of this call are lost.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+       { name => 'offx', type => 'int32',
+         desc => 'x offset for translation' },
+       { name => 'offy', type => 'int32',
+         desc => 'y offset for translation' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_item_translate (GIMP_ITEM (gimp_image_get_mask (image)),
+                       offx, offy, TRUE);
+}
+CODE
+    );
+}
+
+sub selection_float {
+    $blurb = <<'BLURB';
+Float the selection from the specified drawable with initial offsets as
+specified.
+BLURB
+
+    $help = <<'HELP';
+This procedure determines the region of the specified drawable that lies
+beneath the current selection. The region is then cut from the drawable and the
+resulting data is made into a new layer which is instantiated as a floating
+selection. The offsets allow initial positioning of the new floating selection.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The drawable from which to float selection' },
+       { name => 'offx', type => 'int32',
+         desc => 'x offset for translation' },
+       { name => 'offy', type => 'int32',
+         desc => 'y offset for translation' }
+    );
+
+    @outargs = (
+       { name => 'layer', type => 'layer', wrap => 1,
+         desc => 'The floated layer' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                 GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
+    {
+      GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable));
+
+      layer = gimp_selection_float (GIMP_SELECTION (gimp_image_get_mask (image)),
+                                    drawable, context, TRUE, offx, offy,
+                                    error);
+      if (! layer)
+        success = FALSE;
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub selection_invert {
+    $blurb = 'Invert the selection mask.';
+
+    $help = <<'HELP';
+This procedure inverts the selection mask. For every pixel in the selection
+channel, its new value is calculated as (255 - old-value).
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_channel_invert (gimp_image_get_mask (image), TRUE);
+}
+CODE
+    );
+}
+
+sub selection_sharpen {
+    $blurb = 'Sharpen the selection mask.';
+
+    $help = <<'HELP';
+This procedure sharpens the selection mask. For every pixel in the selection
+channel, if the value is > 127, the new pixel is assigned a value of 255.
+This removes any "anti-aliasing" that might exist in the selection mask's
+boundary.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_channel_sharpen (gimp_image_get_mask (image), TRUE);
+}
+CODE
+    );
+}
+
+sub selection_all {
+    $blurb = 'Select all of the image.';
+
+    $help = <<'HELP';
+This procedure sets the selection mask to completely encompass the image.
+Every pixel in the selection channel is set to 255.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_channel_all (gimp_image_get_mask (image), TRUE);
+}
+CODE
+    );
+}
+
+sub selection_none {
+    $blurb = 'Deselect the entire image.';
+
+    $help = <<'HELP';
+This procedure deselects the entire image. Every pixel in the selection channel
+is set to 0.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_channel_clear (gimp_image_get_mask (image), NULL, TRUE);
+}
+CODE
+    );
+}
+
+sub selection_feather {
+    $blurb = "Feather the image's selection";
+
+    $help = <<'HELP';
+This procedure feathers the selection. Feathering is implemented
+using a gaussian blur.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+       { name => 'radius', type => '0 <= float',
+         desc => 'Radius of feather (in pixels)' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_channel_feather (gimp_image_get_mask (image),
+                        radius, radius, TRUE);
+}
+CODE
+    );
+}
+
+sub selection_border {
+    $blurb = "Border the image's selection";
+
+    $help .= <<'HELP';
+This procedure borders the selection. Bordering creates a new
+selection which is defined along the boundary of the previous
+selection at every point within the specified radius.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+       { name => 'radius', type => '0 <= int32',
+         desc => 'Radius of border (in pixels)' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  /* FIXME: "style" and "edge-lock" hardcoded to SMOOTH and TRUE, respectively. */
+  gimp_channel_border (gimp_image_get_mask (image),
+                       radius, radius,
+                       GIMP_CHANNEL_BORDER_STYLE_SMOOTH,
+                       TRUE, TRUE);
+}
+CODE
+    );
+}
+
+sub selection_grow {
+    $blurb = "Grow the image's selection";
+
+    $help .= <<'HELP';
+This procedure grows the selection. Growing involves expanding the
+boundary in all directions by the specified pixel amount.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+       { name => 'steps', type => '0 <= int32',
+         desc => 'Steps of grow (in pixels)' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_channel_grow (gimp_image_get_mask (image),
+                     steps, steps, TRUE);
+}
+CODE
+    );
+}
+
+sub selection_shrink {
+    $blurb = "Shrink the image's selection";
+
+    $help .= <<'HELP';
+This procedure shrinks the selection. Shrinking involves trimming the
+existing selection boundary on all sides by the specified number of
+pixels.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+       { name => 'steps', type => '0 <= int32',
+         desc => 'Steps of shrink (in pixels)' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_channel_shrink (gimp_image_get_mask (image),
+                       steps, steps, FALSE, TRUE);
+}
+CODE
+    );
+}
+
+sub selection_flood {
+    $blurb = "Remove holes from the image's selection";
+
+    $help .= <<'HELP';
+This procedure removes holes from the selection, that can come from 
+selecting a patchy area with the Fuzzy Select Tool. 
+In technical terms this procedure floods the selection. 
+See the Algorithms page in the developer wiki for details.
+HELP
+
+    $author = 'Ell';
+    $copyright = 'Ell';
+    $date = '2016';
+    $since = '2.10';
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_channel_flood (gimp_image_get_mask (image), TRUE);
+}
+CODE
+    );
+}
+
+sub selection_layer_alpha {
+    &std_pdb_deprecated ('gimp-image-select-item');
+
+    @inargs = (
+       { name => 'layer', type => 'layer',
+         desc => 'Layer with alpha' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (layer), NULL, 0, error))
+    gimp_item_to_selection (GIMP_ITEM (layer),
+                            GIMP_CHANNEL_OP_REPLACE,
+                            TRUE, FALSE, 0.0, 0.0);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub selection_load {
+    &std_pdb_deprecated ('gimp-image-select-item');
+
+    @inargs = (
+       { name => 'channel', type => 'channel',
+         desc => 'The channel' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (channel), NULL, 0, error))
+    gimp_item_to_selection (GIMP_ITEM (channel),
+                            GIMP_CHANNEL_OP_REPLACE,
+                            TRUE, FALSE, 0.0, 0.0);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub selection_combine {
+    &std_pdb_deprecated ('gimp-image-select-item');
+
+    @inargs = (
+       { name => 'channel', type => 'channel',
+         desc => 'The channel' },
+       { name => 'operation', type => 'enum GimpChannelOps',
+         desc => 'The selection operation' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (channel), NULL, 0, error))
+    gimp_item_to_selection (GIMP_ITEM (channel),
+                            operation,
+                            TRUE, FALSE, 0.0, 0.0);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub selection_save {
+    $blurb = 'Copy the selection mask to a new channel.';
+
+    $help = <<'HELP';
+This procedure copies the selection mask and stores the content in a new
+channel. The new channel is automatically inserted into the image's list of
+channels.
+HELP
+
+    &std_pdb_misc;
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' }
+    );
+
+    @outargs = (
+       { name => 'channel', type => 'channel',
+         desc => 'The new channel' }
+    );
+
+    %invoke = (
+        headers => [qw("core/gimpselection.h") ],
+       code    => <<'CODE'
+{
+  channel = gimp_selection_save (GIMP_SELECTION (gimp_image_get_mask (image)));
+
+  if (! channel)
+    success = FALSE;
+}
+CODE
+    );
+}
+
+@headers = qw("libgimpmath/gimpmath.h"
+              "core/gimppickable.h"
+              "gimppdb-utils.h"
+              "gimp-intl.h");
+
+@procs = qw(selection_bounds selection_value selection_is_empty
+           selection_translate selection_float
+           selection_invert selection_sharpen selection_all selection_none
+           selection_feather selection_border selection_grow selection_shrink
+           selection_flood selection_layer_alpha selection_load selection_save
+           selection_combine);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Image mask';
+$doc_title = 'gimpselection';
+$doc_short_desc = 'Functions for manipulating selections.';
+$doc_long_desc = 'Functions for manipulating selections.';
+
+1;
diff --git a/pdb/groups/selection_tools.pdb b/pdb/groups/selection_tools.pdb
new file mode 100644
index 0000000..6bf398e
--- /dev/null
+++ b/pdb/groups/selection_tools.pdb
@@ -0,0 +1,419 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub by_color_select {
+    &std_pdb_deprecated ('gimp-image-select-color');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+       { name => 'color', type => 'color',
+         desc => 'The color to select' },
+       { name => 'threshold', type => '0 <= int32 <= 255',
+         desc => 'Threshold in intensity levels' },
+       { name => 'operation', type => 'enum GimpChannelOps',
+         desc => 'The selection operation' },
+       { name => 'antialias', type => 'boolean',
+         desc => 'Antialiasing' },
+       { name => 'feather', type => 'boolean',
+         desc => 'Feather option for selections' },
+       { name => 'feather_radius', type => '0 <= float',
+         desc => 'Radius for feather operation' },
+       { name => 'sample_merged', type => 'boolean',
+         desc => 'Use the composite image, not the drawable' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable));
+
+  gimp_channel_select_by_color (gimp_image_get_mask (image), drawable,
+                                sample_merged,
+                                &color,
+                                threshold / 255.0,
+                                FALSE /* don't select transparent */,
+                                GIMP_SELECT_CRITERION_COMPOSITE,
+                                operation,
+                                antialias,
+                                feather,
+                                feather_radius,
+                                feather_radius);
+}
+CODE
+    );
+}
+
+
+sub by_color_select_full {
+    &std_pdb_deprecated ('gimp-image-select-color');
+    &david_pdb_misc('2006','2.4');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+       { name => 'color', type => 'color',
+         desc => 'The color to select' },
+       { name => 'threshold', type => '0 <= int32 <= 255',
+         desc => 'Threshold in intensity levels' },
+       { name => 'operation', type => 'enum GimpChannelOps',
+         desc => 'The selection operation' },
+       { name => 'antialias', type => 'boolean',
+         desc => 'Antialiasing' },
+       { name => 'feather', type => 'boolean',
+         desc => 'Feather option for selections' },
+       { name => 'feather_radius_x', type => '0 <= float',
+         desc => 'Radius for feather operation in X direction' },
+       { name => 'feather_radius_y', type => '0 <= float',
+         desc => 'Radius for feather operation in Y direction' },
+       { name => 'sample_merged', type => 'boolean',
+         desc => 'Use the composite image, not the drawable' },
+        { name => 'select_transparent', type => 'boolean',
+          desc => "Whether to consider transparent pixels for selection.
+                   If TRUE, transparency is considered as a unique selectable
+                   color." },
+        { name => 'select_criterion', type => 'enum GimpSelectCriterion',
+          desc => "The criterion used to determine color similarity.
+                   SELECT_CRITERION_COMPOSITE is the standard choice.
+                  " },
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable));
+
+  gimp_channel_select_by_color (gimp_image_get_mask (image), drawable,
+                                sample_merged,
+                                &color,
+                                threshold / 255.0,
+                               select_transparent,
+                                select_criterion,
+                                operation,
+                                antialias,
+                                feather,
+                                feather_radius_x,
+                                feather_radius_y);
+}
+CODE
+    );
+}
+
+
+sub ellipse_select {
+    &std_pdb_deprecated ('gimp-image-select-ellipse');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+       { name => 'x', type => 'float',
+         desc => 'x coordinate of upper-left corner of ellipse bounding box' },
+       { name => 'y', type => 'float',
+         desc => 'y coordinate of upper-left corner of ellipse bounding box' },
+       { name => 'width', type => '0 < float',
+         desc => 'The width of the ellipse' },
+       { name => 'height', type => '0 < float',
+         desc => 'The height of the ellipse' },
+       { name => 'operation', type => 'enum GimpChannelOps',
+         desc => 'The selection operation' },
+       { name => 'antialias', type => 'boolean',
+         desc => 'Antialiasing' },
+       { name => 'feather', type => 'boolean',
+         desc => 'Feather option for selections' },
+       { name => 'feather_radius', type => '0 <= float',
+         desc => 'Radius for feather operation' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_channel_select_ellipse (gimp_image_get_mask (image),
+                               (gint) x, (gint) y,
+                               (gint) width, (gint) height,
+                               operation,
+                               antialias,
+                               feather,
+                               feather_radius,
+                               feather_radius,
+                               TRUE);
+}
+CODE
+    );
+}
+
+
+sub free_select {
+    &std_pdb_deprecated ('gimp-image-select-polygon');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+        { name => 'segs', type => 'floatarray',
+          desc => 'Array of points: { p1.x, p1.y, p2.x, p2.y, ...,
+                   pn.x, pn.y}',
+          array => { type => '2 <= int32',
+                     desc => 'Number of points (count 1 coordinate as two
+                              points)' } },
+       { name => 'operation', type => 'enum GimpChannelOps',
+         desc => 'The selection operation' },
+       { name => 'antialias', type => 'boolean',
+         desc => 'Antialiasing' },
+       { name => 'feather', type => 'boolean',
+         desc => 'Feather option for selections' },
+       { name => 'feather_radius', type => '0 <= float',
+         desc => 'Radius for feather operation' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  gimp_channel_select_polygon (gimp_image_get_mask (image),
+                               _("Free Select"),
+                               num_segs / 2,
+                               (GimpVector2 *) segs,
+                               operation,
+                               antialias,
+                               feather,
+                               feather_radius,
+                               feather_radius,
+                               TRUE);
+}
+CODE
+    );
+}
+
+
+sub fuzzy_select {
+    &std_pdb_deprecated ('gimp-image-select-contiguous-color');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+       { name => 'x', type => 'float',
+         desc => 'x coordinate of initial seed fill point: (image
+                  coordinates)' },
+       { name => 'y', type => 'float',
+         desc => 'y coordinate of initial seed fill point: (image
+                  coordinates)' },
+       { name => 'threshold', type => '0 <= int32 <= 255',
+         desc => 'Threshold in intensity levels' },
+       { name => 'operation', type => 'enum GimpChannelOps',
+         desc => 'The selection operation' },
+       { name => 'antialias', type => 'boolean',
+         desc => 'Antialiasing' },
+       { name => 'feather', type => 'boolean',
+         desc => 'Feather option for selections' },
+       { name => 'feather_radius', type => '0 <= float',
+         desc => 'Radius for feather operation' },
+       { name => 'sample_merged', type => 'boolean',
+         desc => 'Use the composite image, not the drawable' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable));
+
+  gimp_channel_select_fuzzy (gimp_image_get_mask (image),
+                             drawable,
+                             sample_merged,
+                             x, y,
+                             threshold / 255.0,
+                             FALSE /* don't select transparent */,
+                             GIMP_SELECT_CRITERION_COMPOSITE,
+                             FALSE /* no diagonal neighbors */,
+                             operation,
+                             antialias,
+                             feather,
+                             feather_radius,
+                             feather_radius);
+}
+CODE
+    );
+}
+
+
+sub fuzzy_select_full {
+    &std_pdb_deprecated ('gimp-image-select-contiguous-color');
+    &david_pdb_misc("2006","2.4");
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+       { name => 'x', type => 'float',
+         desc => 'x coordinate of initial seed fill point: (image
+                  coordinates)' },
+       { name => 'y', type => 'float',
+         desc => 'y coordinate of initial seed fill point: (image
+                  coordinates)' },
+       { name => 'threshold', type => '0 <= int32 <= 255',
+         desc => 'Threshold in intensity levels' },
+       { name => 'operation', type => 'enum GimpChannelOps',
+         desc => 'The selection operation' },
+       { name => 'antialias', type => 'boolean',
+         desc => 'Antialiasing' },
+       { name => 'feather', type => 'boolean',
+         desc => 'Feather option for selections' },
+       { name => 'feather_radius_x', type => '0 <= float',
+         desc => 'Radius for feather operation in X direction' },
+       { name => 'feather_radius_y', type => '0 <= float',
+         desc => 'Radius for feather operation in Y direction' },
+       { name => 'sample_merged', type => 'boolean',
+         desc => 'Use the composite image, not the drawable' },
+       { name => 'select_transparent', type => 'boolean',
+          desc => "Whether to consider transparent pixels for selection.
+                   If TRUE, transparency is considered as a unique selectable
+                   color." },
+        { name => 'select_criterion', type => 'enum GimpSelectCriterion',
+          desc => "The criterion used to determine color similarity.
+                   SELECT_CRITERION_COMPOSITE is the standard choice.
+                  " },
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable));
+
+  gimp_channel_select_fuzzy (gimp_image_get_mask (image),
+                             drawable,
+                             sample_merged,
+                             x, y,
+                             threshold / 255.0,
+                             select_transparent,
+                             select_criterion,
+                             FALSE /* no diagonal neighbors */,
+                             operation,
+                             antialias,
+                             feather,
+                             feather_radius_x,
+                             feather_radius_y);
+}
+CODE
+    );
+}
+
+
+sub rect_select {
+    &std_pdb_deprecated ('gimp-image-select-rectangle');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+       { name => 'x', type => 'float',
+         desc => 'x coordinate of upper-left corner of rectangle' },
+       { name => 'y', type => 'float',
+         desc => 'y coordinate of upper-left corner of rectangle' },
+       { name => 'width', type => '0 < float',
+         desc => 'The width of the rectangle' },
+       { name => 'height', type => '0 < float',
+         desc => 'The height of the rectangle' },
+       { name => 'operation', type => 'enum GimpChannelOps',
+         desc => 'The selection operation' },
+       { name => 'feather', type => 'boolean',
+         desc => 'Feather option for selections' },
+       { name => 'feather_radius', type => '0 <= float',
+         desc => 'Radius for feather operation' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_channel_select_rectangle (gimp_image_get_mask (image),
+                                 (gint) x, (gint) y,
+                                 (gint) width, (gint) height,
+                                 operation,
+                                 feather,
+                                 feather_radius,
+                                 feather_radius,
+                                 TRUE);
+}
+CODE
+    );
+}
+
+
+sub round_rect_select {
+    &std_pdb_deprecated ('gimp-image-select-round-rectangle');
+    &martin_pdb_misc("2006","2.4");
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+       { name => 'x', type => 'float',
+         desc => 'x coordinate of upper-left corner of rectangle' },
+       { name => 'y', type => 'float',
+         desc => 'y coordinate of upper-left corner of rectangle' },
+       { name => 'width', type => '0 < float',
+         desc => 'The width of the rectangle' },
+       { name => 'height', type => '0 < float',
+         desc => 'The height of the rectangle' },
+        { name => 'corner_radius_x', type => '0 < float < GIMP_MAX_IMAGE_SIZE',
+          desc => 'The corner radius in X direction' },
+        { name => 'corner_radius_y', type => '0 < float < GIMP_MAX_IMAGE_SIZE',
+          desc => 'The corner radius in Y direction' },
+       { name => 'operation', type => 'enum GimpChannelOps',
+         desc => 'The selection operation' },
+       { name => 'antialias', type => 'boolean',
+         desc => 'Antialiasing' },
+       { name => 'feather', type => 'boolean',
+         desc => 'Feather option for selections' },
+       { name => 'feather_radius_x', type => '0 <= float',
+         desc => 'Radius for feather operation in X direction' },
+       { name => 'feather_radius_y', type => '0 <= float',
+         desc => 'Radius for feather operation in Y direction' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gimp_channel_select_round_rect (gimp_image_get_mask (image),
+                                  (gint) x, (gint) y,
+                                  (gint) width, (gint) height,
+                                  corner_radius_x,
+                                  corner_radius_y,
+                                  operation,
+                                  antialias,
+                                  feather,
+                                  feather_radius_x,
+                                  feather_radius_y,
+                                  TRUE);
+}
+CODE
+    );
+}
+
+
+@headers = qw("libgimpbase/gimpbase.h"
+              "core/gimpchannel-select.h"
+              "gimp-intl.h");
+
+@procs = qw(by_color_select by_color_select_full
+            ellipse_select
+            free_select 
+           fuzzy_select fuzzy_select_full
+           rect_select round_rect_select);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Selection Tool procedures';
+$doc_title = 'gimpselectiontools';
+$doc_short_desc = 'Access to toolbox selection tools.';
+$doc_long_desc = 'Functions giving access to toolbox selection tools.';
+
+1;
diff --git a/pdb/groups/text_layer.pdb b/pdb/groups/text_layer.pdb
new file mode 100644
index 0000000..1328ee0
--- /dev/null
+++ b/pdb/groups/text_layer.pdb
@@ -0,0 +1,1253 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# New Text PDB API
+# Copyright (C) 2008 Marcus Heese <heese cip ifi lmu de>
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+
+sub text_layer_new {
+    $blurb = 'Creates a new text layer.';
+
+    $help = <<'HELP';
+This procedure creates a new text layer. The arguments are kept as
+simple as necessary for the normal case. All text attributes, however,
+can be modified with the appropriate gimp_text_layer_set_*()
+procedures. The new layer still needs to be added to the image, as
+this is not automatic. Add the new layer using gimp_image_insert_layer().
+HELP
+
+    &marcus_pdb_misc('2008', '2.6');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'text', type => 'string',
+          desc => 'The text to generate (in UTF-8 encoding)' },
+        { name => 'fontname', type => 'string',
+          desc => 'The name of the font' },
+        { name => 'size', type => '0.0 <= float <= 8192.0',
+          desc => 'The size of text in either pixels or points' },
+        { name => 'unit', type => 'unit',
+          desc => 'The units of specified size' }
+    );
+
+    @outargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The new text layer.' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  GimpText *gimp_text;
+  GimpRGB   color;
+
+  gimp_context_get_foreground (context, &color);
+  
+  gimp_text = g_object_new (GIMP_TYPE_TEXT,
+                            "text",           text,
+                            "font",           fontname,
+                            "font-size",      size,
+                            "font-size-unit", unit,
+                            "color",          &color,
+                            NULL);
+  
+  layer = gimp_text_layer_new (image, gimp_text);
+  g_object_unref (gimp_text);
+
+  if (! layer)
+    {
+      g_set_error (error, GIMP_PDB_ERROR, GIMP_PDB_ERROR_INVALID_ARGUMENT,
+                   _("Failed to create text layer"));
+
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub text_layer_get_text {
+    $blurb = 'Get the text from a text layer as string.';
+
+    $help = <<'HELP';
+This procedure returns the text from a text layer as a string.
+HELP
+
+    &marcus_pdb_misc('2008', '2.6');
+
+    @inargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The text layer' }
+    );
+
+    @outargs = (
+        { name => 'text', type => 'string',
+          desc => 'The text from the specified text layer.' }
+    );
+    
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_layer_is_text_layer (layer, 0, error))
+    {
+      g_object_get (gimp_text_layer_get_text (GIMP_TEXT_LAYER (layer)),
+                    "text", &text,
+                    NULL);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub text_layer_set_text {
+    $blurb = 'Set the text of a text layer.';
+
+    $help = <<'HELP';
+This procedure changes the text of a text layer.
+HELP
+
+    &marcus_pdb_misc('2008', '2.6');
+
+    @inargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The text layer' },
+        { name => 'text', type => 'string',
+          desc => 'The new text to set' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_layer_is_text_layer (layer, GIMP_PDB_ITEM_CONTENT, error))
+    {
+      gimp_text_layer_set (GIMP_TEXT_LAYER (layer),
+                           _("Set text layer attribute"),
+                           "text", text,
+                           NULL);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub text_layer_get_markup {
+    $blurb = 'Get the markup from a text layer as string.';
+
+    $help = <<'HELP';
+This procedure returns the markup of the styles from a text layer.
+The markup will be in the form of Pango's markup - See
+http://www.pango.org/ for more information about Pango and its markup.
+Note: Setting the markup of a text layer using Pango's markup is not
+supported for now.
+HELP
+
+    barak_pdb_misc('2010', '2.8');
+
+    @inargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The text layer' }
+    );
+
+    @outargs = (
+        { name => 'markup', type => 'string',
+          desc => 'The markup which represents the style of the specified text layer.' }
+    );
+    
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_layer_is_text_layer (layer, 0, error))
+    {
+      g_object_get (gimp_text_layer_get_text (GIMP_TEXT_LAYER (layer)),
+                    "markup", &markup,
+                    NULL);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub text_layer_get_font {
+    $blurb = 'Get the font from a text layer as string.';
+
+    $help = <<'HELP';
+This procedure returns the name of the font from a text layer.
+HELP
+
+    &marcus_pdb_misc('2008', '2.6');
+
+    @inargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The text layer' }
+    );
+
+    @outargs = (
+        { name => 'font', type => 'string',
+          desc => 'The font which is used in the specified text layer.' }
+    );
+    
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_layer_is_text_layer (layer, 0, error))
+    {
+      g_object_get (gimp_text_layer_get_text (GIMP_TEXT_LAYER (layer)),
+                    "font", &font,
+                    NULL);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub text_layer_set_font {
+    $blurb = 'Set the font of a text layer.';
+
+    $help = <<'HELP';
+This procedure modifies the font used in the specified text layer.
+HELP
+
+    &marcus_pdb_misc('2008', '2.6');
+
+    @inargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The text layer' },
+        { name => 'font', type => 'string',
+          desc => 'The new font to use' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_layer_is_text_layer (layer, GIMP_PDB_ITEM_CONTENT, error))
+    {
+      gimp_text_layer_set (GIMP_TEXT_LAYER (layer),
+                           _("Set text layer attribute"),
+                           "font", font,
+                           NULL);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub text_layer_get_font_size {
+    $blurb = 'Get the font size from a text layer.';
+
+    $help = <<'HELP';
+This procedure returns the size of the font which is used in a text
+layer. You will receive the size as a float 'font-size' in 'unit'
+units.
+HELP
+
+    &marcus_pdb_misc('2008', '2.6');
+
+    @inargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The text layer' }
+    );
+
+    @outargs = (
+        { name => 'font_size', type => 'float',
+          desc => 'The font size' },
+        { name => 'unit', type => 'unit',
+          desc => 'The unit used for the font size' }
+    );
+    
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_layer_is_text_layer (layer, 0, error))
+    {
+      g_object_get (gimp_text_layer_get_text (GIMP_TEXT_LAYER (layer)),
+                    "font-size",      &font_size,
+                    "font-size-unit", &unit,
+                    NULL);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub text_layer_set_font_size {
+    $blurb = 'Set the font size.';
+
+    $help = <<'HELP';
+This procedure changes the font size of a text layer. The size of your
+font will be a double 'font-size' of 'unit' units.
+HELP
+
+    &marcus_pdb_misc('2008', '2.6');
+
+    @inargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The text layer' },
+        { name => 'font_size', type => '0.0 <= float <= 8192.0',
+          desc => 'The font size' },
+        { name => 'unit', type => 'unit',
+          desc => 'The unit to use for the font size' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_layer_is_text_layer (layer, GIMP_PDB_ITEM_CONTENT, error))
+    {
+      gimp_text_layer_set (GIMP_TEXT_LAYER (layer),
+                           _("Set text layer attribute"),
+                           "font-size-unit", unit,
+                           "font-size",      font_size,
+                           NULL);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub text_layer_get_hinting {
+    &std_pdb_deprecated ('gimp-text-layer-get-hint-style');
+
+    @inargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The text layer' }
+    );
+
+    @outargs = (
+        { name => 'hinting', type => 'boolean',
+          desc => 'A flag which is true if hinting is used on the font.' },
+        { name => 'autohint', type => 'boolean',
+          desc => 'A flag which is true if the text layer is forced to use the autohinter from FreeType.' }
+    );
+    
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_layer_is_text_layer (layer, 0, error))
+    {
+      g_object_get (gimp_text_layer_get_text (GIMP_TEXT_LAYER (layer)),
+                    "hinting", &hinting,
+                    NULL);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub text_layer_set_hinting {
+    &std_pdb_deprecated ('gimp-text-layer-set-hint-style');
+
+    $blurb = 'Enable/disable the use of hinting in a text layer.';
+
+    $help = <<'HELP';
+This procedure enables or disables hinting on the text of a text
+layer. If you enable 'auto-hint', FreeType\'s automatic hinter will be
+used and hinting information from the font will be ignored.
+HELP
+
+    &marcus_pdb_misc('2008', '2.6');
+
+    @inargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The text layer' },
+        { name => 'hinting', type => 'boolean',
+          desc => 'Enable/disable the use of hinting on the text' },
+        { name => 'autohint', type => 'boolean', dead => 1,
+          desc => 'Force the use of the autohinter provided through FreeType' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_layer_is_text_layer (layer, GIMP_PDB_ITEM_CONTENT, error))
+    {
+      gimp_text_layer_set (GIMP_TEXT_LAYER (layer),
+                           _("Set text layer attribute"),
+                           "hinting", hinting,
+                           NULL);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub text_layer_get_antialias {
+    $blurb = 'Check if antialiasing is used in the text layer.';
+
+    $help = <<'HELP';
+This procedure checks if antialiasing is enabled in the specified text layer.
+HELP
+
+    &marcus_pdb_misc('2008', '2.6');
+
+    @inargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The text layer' }
+    );
+
+    @outargs = (
+        { name => 'antialias', type => 'boolean',
+          desc => 'A flag which is true if antialiasing is used for rendering the font in the text layer.' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_layer_is_text_layer (layer, 0, error))
+    {
+      g_object_get (gimp_text_layer_get_text (GIMP_TEXT_LAYER (layer)),
+                    "antialias", &antialias,
+                    NULL);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub text_layer_set_antialias {
+    $blurb = 'Enable/disable anti-aliasing in a text layer.';
+
+    $help = <<'HELP';
+This procedure enables or disables anti-aliasing of the text in a text layer.
+HELP
+
+    &marcus_pdb_misc('2008', '2.6');
+
+    @inargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The text layer' },
+        { name => 'antialias', type => 'boolean',
+          desc => 'Enable/disable antialiasing of the text' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_layer_is_text_layer (layer, GIMP_PDB_ITEM_CONTENT, error))
+    {
+      gimp_text_layer_set (GIMP_TEXT_LAYER (layer),
+                           _("Set text layer attribute"),
+                           "antialias", antialias,
+                           NULL);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub text_layer_get_hint_style {
+    $blurb = 'Get information about hinting in the specified text layer.';
+
+    $help = <<'HELP';
+This procedure provides information about the hinting that is being
+used in a text layer. Hinting can be optimized for fidelity or contrast
+or it can be turned entirely off.
+HELP
+
+    &marcus_pdb_misc('2008', '2.8');
+
+    @inargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The text layer' }
+    );
+
+    @outargs = (
+        { name => 'style', type => 'enum GimpTextHintStyle',
+          desc => 'The hint style used for font outlines' }
+    );
+    
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_layer_is_text_layer (layer, 0, error))
+    {
+      g_object_get (gimp_text_layer_get_text (GIMP_TEXT_LAYER (layer)),
+                    "hint-style", &style,
+                    NULL);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub text_layer_set_hint_style {
+    $blurb = 'Control how font outlines are hinted in a text layer.';
+
+    $help = <<'HELP';
+This procedure sets the hint style for font outlines in a text
+layer. This controls whether to fit font outlines to the pixel grid,
+and if so, whether to optimize for fidelity or contrast.
+HELP
+
+    &neo_pdb_misc('2008', '2.8');
+
+    @inargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The text layer' },
+        { name => 'style', type => 'enum GimpTextHintStyle',
+          desc => 'The new hint style' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_layer_is_text_layer (layer, GIMP_PDB_ITEM_CONTENT, error))
+    {
+      gimp_text_layer_set (GIMP_TEXT_LAYER (layer),
+                           _("Set text layer attribute"),
+                           "hint-style", style,
+                           NULL);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub text_layer_get_kerning {
+    $blurb = 'Check if kerning is used in the text layer.';
+
+    $help = <<'HELP';
+This procedure checks if kerning is enabled in the specified text layer.
+HELP
+
+    &marcus_pdb_misc('2008', '2.6');
+
+    @inargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The text layer' }
+    );
+
+    @outargs = (
+        { name => 'kerning', type => 'boolean',
+          desc => 'A flag which is true if kerning is used in the text layer.' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_layer_is_text_layer (layer, 0, error))
+    {
+      g_object_get (gimp_text_layer_get_text (GIMP_TEXT_LAYER (layer)),
+                    "kerning", &kerning,
+                    NULL);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub text_layer_set_kerning {
+    $blurb = 'Enable/disable kerning in a text layer.';
+
+    $help = <<'HELP';
+This procedure enables or disables kerning in a text layer.
+HELP
+
+    &marcus_pdb_misc('2008', '2.6');
+
+    @inargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The text layer' },
+        { name => 'kerning', type => 'boolean',
+          desc => 'Enable/disable kerning in the text' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_layer_is_text_layer (layer, GIMP_PDB_ITEM_CONTENT, error))
+    {
+      gimp_text_layer_set (GIMP_TEXT_LAYER (layer),
+                           _("Set text layer attribute"),
+                           "kerning", kerning,
+                           NULL);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub text_layer_get_language {
+    $blurb = 'Get the language used in the text layer.';
+
+    $help = <<'HELP';
+This procedure returns the language string which is set for the text
+in the text layer.
+HELP
+
+    &marcus_pdb_misc('2008', '2.6');
+
+    @inargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The text layer.' }
+    );
+
+    @outargs = (
+        { name => 'language', type => 'string',
+          desc => 'The language used in the text layer.' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_layer_is_text_layer (layer, 0, error))
+    {
+      g_object_get (gimp_text_layer_get_text (GIMP_TEXT_LAYER (layer)),
+                    "language", &language,
+                    NULL);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub text_layer_set_language {
+    $blurb = 'Set the language of the text layer.';
+
+    $help = <<'HELP';
+This procedure sets the language of the text in text layer. For some
+scripts the language has an influence of how the text is rendered.
+HELP
+
+    &marcus_pdb_misc('2008', '2.6');
+
+    @inargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The text layer' },
+        { name => 'language', type => 'string',
+          desc => 'The new language to use for the text layer' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_layer_is_text_layer (layer, GIMP_PDB_ITEM_CONTENT, error))
+    {
+      gimp_text_layer_set (GIMP_TEXT_LAYER (layer),
+                           _("Set text layer attribute"),
+                           "language", language,
+                           NULL);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub text_layer_get_base_direction {
+    $blurb = 'Get the base direction used for rendering the text layer.';
+
+    $help = <<'HELP';
+This procedure returns the base direction used for rendering the text
+in the text layer
+HELP
+
+    &marcus_pdb_misc('2008', '2.6');
+
+    @inargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The text layer.' }
+    );
+
+    @outargs = (
+        { name => 'direction', type => 'enum GimpTextDirection',
+          desc => 'The based direction used for the text layer.' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_layer_is_text_layer (layer, 0, error))
+    {
+      g_object_get (gimp_text_layer_get_text (GIMP_TEXT_LAYER (layer)),
+                    "base-direction", &direction,
+                    NULL);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub text_layer_set_base_direction {
+    $blurb = 'Set the base direction in the text layer.';
+
+    $help = <<'HELP';
+This procedure sets the base direction used in applying the Unicode
+bidirectional algorithm when rendering the text.
+HELP
+
+    &marcus_pdb_misc('2008', '2.6');
+
+    @inargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The text layer' },
+        { name => 'direction', type => 'enum GimpTextDirection',
+          desc => 'The base direction of the text.' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_layer_is_text_layer (layer, GIMP_PDB_ITEM_CONTENT, error))
+    {
+      gimp_text_layer_set (GIMP_TEXT_LAYER (layer),
+                           _("Set text layer attribute"),
+                           "base-direction", direction,
+                           NULL);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub text_layer_get_justification {
+    $blurb = 'Get the text justification information of the text layer.';
+
+    $help = <<'HELP';
+This procedure returns the alignment of the lines in the text layer
+relative to each other.
+HELP
+
+    &marcus_pdb_misc('2008', '2.6');
+
+    @inargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The text layer.' }
+    );
+
+    @outargs = (
+        { name => 'justify', type => 'enum GimpTextJustification',
+          desc => 'The justification used in the text layer.' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_layer_is_text_layer (layer, 0, error))
+    {
+      g_object_get (gimp_text_layer_get_text (GIMP_TEXT_LAYER (layer)),
+                    "justify", &justify,
+                    NULL);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub text_layer_set_justification {
+    $blurb = 'Set the justification of the text in a text layer.';
+
+    $help = <<'HELP';
+This procedure sets the alignment of the lines in the text layer
+relative to each other.
+HELP
+
+    &marcus_pdb_misc('2008', '2.6');
+
+    @inargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The text layer' },
+        { name => 'justify', type => 'enum GimpTextJustification',
+          desc => 'The justification for your text.' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_layer_is_text_layer (layer, GIMP_PDB_ITEM_CONTENT, error))
+    {
+      gimp_text_layer_set (GIMP_TEXT_LAYER (layer),
+                           _("Set text layer attribute"),
+                           "justify", justify,
+                           NULL);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub text_layer_get_color {
+    $blurb = 'Get the color of the text in a text layer.';
+
+    $help = <<'HELP';
+This procedure returns the color of the text in a text layer.
+HELP
+
+    &marcus_pdb_misc('2008', '2.6');
+
+    @inargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The text layer.' }
+    );
+
+    @outargs = (
+        { name => 'color', type => 'color', void_ret => 1,
+          desc => 'The color of the text.' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_layer_is_text_layer (layer, 0, error))
+    {
+      color = gimp_text_layer_get_text (GIMP_TEXT_LAYER (layer))->color;
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub text_layer_set_color {
+    $blurb = 'Set the color of the text in the text layer.';
+
+    $help = <<'HELP';
+This procedure sets the text color in the text layer 'layer'.
+HELP
+
+    &marcus_pdb_misc('2008', '2.6');
+
+    @inargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The text layer' },
+        { name => 'color', type => 'color',
+          desc => 'The color to use for the text' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_layer_is_text_layer (layer, GIMP_PDB_ITEM_CONTENT, error))
+    {
+      gimp_text_layer_set (GIMP_TEXT_LAYER (layer),
+                           _("Set text layer attribute"),
+                           "color", &color,
+                           NULL);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub text_layer_get_indent {
+    $blurb = 'Get the line indentation of text layer.';
+
+    $help = <<'HELP';
+This procedure returns the indentation of the first line in a text layer.
+HELP
+
+    &marcus_pdb_misc('2008', '2.6');
+
+    @inargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The text layer.' }
+    );
+
+    @outargs = (
+        { name => 'indent', type => 'float',
+          desc => 'The indentation value of the first line.' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_layer_is_text_layer (layer, 0, error))
+    {
+      g_object_get (gimp_text_layer_get_text (GIMP_TEXT_LAYER (layer)),
+                    "indent", &indent,
+                    NULL);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub text_layer_set_indent {
+    $blurb = 'Set the indentation of the first line in a text layer.';
+
+    $help = <<'HELP';
+This procedure sets the indentation of the first line in the text layer.
+HELP
+
+    &marcus_pdb_misc('2008', '2.6');
+
+    @inargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The text layer' },
+        { name => 'indent', type => '-8192.0 <= float <= 8192.0',
+          desc => 'The indentation for the first line.' }
+     );
+
+     %invoke = (
+         code => <<'CODE'
+{
+  if (gimp_pdb_layer_is_text_layer (layer, GIMP_PDB_ITEM_CONTENT, error))
+    {
+      gimp_text_layer_set (GIMP_TEXT_LAYER (layer),
+                           _("Set text layer attribute"),
+                           "indent", indent,
+                           NULL);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub text_layer_get_line_spacing {
+    $blurb = 'Get the spacing between lines of text.';
+
+    $help = <<'HELP';
+This procedure returns the line-spacing between lines of text in a text layer.
+HELP
+
+    &marcus_pdb_misc('2008', '2.6');
+
+    @inargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The text layer.' }
+    );
+
+    @outargs = (
+        { name => 'line_spacing', type => 'float',
+          desc => 'The line-spacing value.' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_layer_is_text_layer (layer, 0, error))
+    {
+      g_object_get (gimp_text_layer_get_text (GIMP_TEXT_LAYER (layer)),
+                    "line-spacing", &line_spacing,
+                    NULL);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub text_layer_set_line_spacing {
+    $blurb = 'Adjust the line spacing in a text layer.';
+
+    $help = <<'HELP';
+This procedure sets the additional spacing used between lines a text layer.
+HELP
+
+    &marcus_pdb_misc('2008', '2.6');
+
+    @inargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The text layer' },
+        { name => 'line_spacing', type => '-8192.0 <= float <= 8192.0',
+          desc => 'The additional line spacing to use.' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_layer_is_text_layer (layer, GIMP_PDB_ITEM_CONTENT, error))
+    {
+      gimp_text_layer_set (GIMP_TEXT_LAYER (layer),
+                           _("Set text layer attribute"),
+                           "line-spacing", line_spacing,
+                           NULL);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub text_layer_get_letter_spacing {
+    $blurb = 'Get the letter spacing used in a text layer.';
+
+    $help = <<'HELP';
+This procedure returns the additional spacing between the single glyphs
+in a text layer.
+HELP
+
+    &marcus_pdb_misc('2008', '2.6');
+
+    @inargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The text layer.' }
+    );
+
+    @outargs = (
+        { name => 'letter_spacing', type => 'float',
+          desc => 'The letter-spacing value.' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_layer_is_text_layer (layer, 0, error))
+    {
+      g_object_get (gimp_text_layer_get_text (GIMP_TEXT_LAYER (layer)),
+                    "letter-spacing", &letter_spacing,
+                    NULL);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub text_layer_set_letter_spacing {
+    $blurb = 'Adjust the letter spacing in a text layer.';
+
+    $help = <<'HELP';
+This procedure sets the additional spacing between the single glyphs
+in a text layer.
+HELP
+
+    &marcus_pdb_misc('2008', '2.6');
+
+    @inargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The text layer' },
+        { name => 'letter_spacing', type => '-8192.0 <= float <= 8192.0',
+          desc => 'The additional letter spacing to use.' }
+    );
+    
+    %invoke = (    
+        code => <<'CODE'
+{
+  if (gimp_pdb_layer_is_text_layer (layer, GIMP_PDB_ITEM_CONTENT, error))
+    {
+      gimp_text_layer_set (GIMP_TEXT_LAYER (layer),
+                           _("Set text layer attribute"),
+                           "letter-spacing", letter_spacing,
+                           NULL);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub text_layer_resize {
+    $blurb = 'Resize the box of a text layer.';
+
+    $help = <<'HELP';
+This procedure changes the width and height of a text layer while
+keeping it as a text layer and not converting it to a bitmap like
+gimp_layer_resize() would do.
+HELP
+
+    barak_pdb_misc('2009', '2.8');
+
+    @inargs = (
+        { name => 'layer', type => 'layer',
+          desc => 'The text layer' },
+        { name => 'width', type => '0.0 <= float <= GIMP_MAX_IMAGE_SIZE',
+          desc => 'The new box width in pixels' },
+        { name => 'height', type => '0.0 <= float <= GIMP_MAX_IMAGE_SIZE',
+          desc => 'The new box height in pixels' },
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_layer_is_text_layer (layer, GIMP_PDB_ITEM_CONTENT, error))
+    {
+      GimpText *text = gimp_text_layer_get_text (GIMP_TEXT_LAYER (layer));
+      gdouble   xres, yres;
+
+      gimp_image_get_resolution (gimp_item_get_image (GIMP_ITEM (layer)),
+                                 &xres, &yres);
+
+      gimp_text_layer_set (GIMP_TEXT_LAYER (layer),
+                           _("Set text layer attribute"),
+                           "box-mode",   GIMP_TEXT_BOX_FIXED,
+                           "box-width",  gimp_pixels_to_units (width,
+                                                               text->box_unit,
+                                                               xres),
+                           "box-height", gimp_pixels_to_units (height,
+                                                               text->box_unit,
+                                                               yres),
+                           NULL);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+
+@headers = qw("libgimpbase/gimpbase.h"
+              "core/gimpcontext.h"
+              "text/gimptext.h"
+              "text/gimptextlayer.h"
+              "gimppdb-utils.h"
+              "gimppdberror.h"
+              "gimp-intl.h");
+
+@procs = qw(text_layer_new
+            text_layer_get_text
+            text_layer_set_text
+            text_layer_get_markup
+            text_layer_get_font
+            text_layer_set_font
+            text_layer_get_font_size
+            text_layer_set_font_size
+            text_layer_get_antialias
+            text_layer_set_antialias
+            text_layer_get_hint_style
+            text_layer_set_hint_style
+            text_layer_get_kerning
+            text_layer_set_kerning
+            text_layer_get_language
+            text_layer_set_language
+            text_layer_get_base_direction
+            text_layer_set_base_direction
+            text_layer_get_justification
+            text_layer_set_justification
+            text_layer_get_color
+            text_layer_set_color
+            text_layer_get_indent
+            text_layer_set_indent
+            text_layer_get_line_spacing
+            text_layer_set_line_spacing
+            text_layer_get_letter_spacing
+            text_layer_set_letter_spacing
+            text_layer_resize
+            text_layer_get_hinting
+            text_layer_set_hinting
+);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Text layer procedures';
+$doc_title = 'gimptextlayer';
+$doc_short_desc = 'Functions for querying and manipulating text layers.';
+$doc_long_desc = 'Functions for querying and manipulating text layers.';
+
+1;
diff --git a/pdb/groups/text_tool.pdb b/pdb/groups/text_tool.pdb
new file mode 100644
index 0000000..f30ac1c
--- /dev/null
+++ b/pdb/groups/text_tool.pdb
@@ -0,0 +1,288 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub text_fontname {
+    $blurb = <<'BLURB';
+Add text at the specified location as a floating selection or a new layer.
+BLURB
+
+    $help = <<'HELP';
+This tool requires a fontname matching an installed PangoFT2 font.
+You can specify the fontsize in units of pixels
+or points, and the appropriate metric is specified using the size_type
+argument. The x and y parameters together control the placement of the new
+text by specifying the upper left corner of the text bounding box. If the
+specified drawable parameter is valid, the text will be created as a floating
+selection attached to the drawable. If the drawable parameter is not valid
+(-1), the text will appear as a new layer. Finally, a border can be specified
+around the final rendered text. The border is measured in pixels. Parameter
+size-type is not used and is currently ignored. If you need to display a font
+in points, divide the size in points by 72.0 and multiply it by the image's
+vertical resolution.
+HELP
+
+    &std_pdb_misc;
+    $author = 'Martin Edlman & Sven Neumann';
+    $date = '1998- 2001';
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'drawable', type => 'drawable',
+          desc => 'The affected drawable: (-1 for a new text layer)',
+          none_ok => 1 },
+        { name => 'x', type => 'float',
+          desc => 'The x coordinate for the left of the text bounding box' },
+        { name => 'y', type => 'float',
+          desc => 'The y coordinate for the top of the text bounding box' },
+        { name => 'text', type => 'string',
+          desc => 'The text to generate (in UTF-8 encoding)' },
+        { name => 'border', type => '-1 <= int32',
+          desc => 'The size of the border' },
+        { name => 'antialias', type => 'boolean',
+          desc => 'Antialiasing' },
+        { name => 'size', type => '0 < float',
+          desc => 'The size of text in either pixels or points' },
+        { name => 'size_type', type => 'enum GimpSizeType', dead => 1,
+          desc => 'The units of specified size' },
+        { name => 'fontname', type => 'string',
+          desc => 'The name of the font' }
+    );
+
+    @outargs = (
+        { name => 'text_layer', type => 'layer',
+          desc => 'The new text layer or -1 if no layer was created.' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (drawable &&
+      (! gimp_pdb_item_is_attached (GIMP_ITEM (drawable), image,
+                                    GIMP_PDB_ITEM_CONTENT, error) ||
+       ! gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error)))
+    success = FALSE;
+
+  if (success)
+    {
+      gchar *real_fontname = g_strdup_printf ("%s %d", fontname, (gint) size);
+
+      text_layer = text_render (image, drawable, context,
+                                x, y, real_fontname, text,
+                                border, antialias);
+
+      g_free (real_fontname);
+    }
+}
+CODE
+    );
+}
+
+sub text_get_extents_fontname {
+    $blurb = 'Get extents of the bounding box for the specified text.';
+
+    $help = <<'HELP';
+This tool returns the width and height of a bounding box for the specified text
+string with the specified font information. Ascent and descent for the
+specified font are returned as well. Parameter size-type is not used and is
+currently ignored. If you need to display a font in points, divide the
+size in points by 72.0 and multiply it by the vertical resolution of the
+image you are taking into account.
+HELP
+
+    &std_pdb_misc;
+    $author = 'Martin Edlman & Sven Neumann';
+    $date = '1998- 2001';
+
+    @inargs = (
+        { name => 'text', type => 'string',
+          desc => 'The text to generate (in UTF-8 encoding)' },
+        { name => 'size', type => '0 < float',
+          desc => 'The size of text in either pixels or points' },
+        { name => 'size_type', type => 'enum GimpSizeType', dead => 1,
+          desc => 'The units of specified size' },
+        { name => 'fontname', type => 'string',
+          desc => 'The name of the font' }
+    );
+
+    @outargs = (
+        { name => 'width', type => 'int32', void_ret => 1,
+          desc => 'The width of the specified font' },
+        { name => 'height', type => 'int32',
+          desc => 'The height of the specified font' },
+        { name => 'ascent', type => 'int32',
+          desc => 'The ascent of the specified font' },
+        { name => 'descent', type => 'int32',
+          desc => 'The descent of the specified font' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  gchar *real_fontname = g_strdup_printf ("%s %d", fontname, (gint) size);
+
+  success = text_get_extents (real_fontname, text,
+                              &width, &height,
+                              &ascent, &descent);
+
+  g_free (real_fontname);
+}
+CODE
+    );
+}
+
+sub text {
+    &std_pdb_deprecated ('gimp-text-fontname');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'drawable', type => 'drawable',
+          desc => 'The affected drawable: (-1 for a new text layer)',
+          none_ok => 1 },
+        { name => 'x', type => 'float',
+          desc => 'The x coordinate for the left of the text bounding box' },
+        { name => 'y', type => 'float',
+          desc => 'The y coordinate for the top of the text bounding box' },
+        { name => 'text', type => 'string',
+          desc => 'The text to generate (in UTF-8 encoding)' },
+        { name => 'border', type => '-1 <= int32',
+          desc => 'The size of the border' },
+        { name => 'antialias', type => 'boolean',
+          desc => 'Antialiasing' },
+        { name => 'size', type => '0 < float',
+          desc => 'The size of text in either pixels or points' },
+        { name => 'size_type', type => 'enum GimpSizeType', dead => 1,
+          desc => 'The units of specified size' },
+        { name => 'foundry', type => 'string', allow_non_utf8 => 1, dead => 1,
+          desc => 'The font foundry' },
+        { name => 'family', type => 'string', allow_non_utf8 => 1,
+          desc => 'The font family' },
+        { name => 'weight', type => 'string', allow_non_utf8 => 1, dead => 1,
+          desc => 'The font weight' },
+        { name => 'slant', type => 'string', allow_non_utf8 => 1, dead => 1,
+          desc => 'The font slant' },
+        { name => 'set_width', type => 'string', allow_non_utf8 => 1, dead => 1,
+          desc => 'The font set-width' },
+        { name => 'spacing', type => 'string', allow_non_utf8 => 1, dead => 1,
+          desc => 'The font spacing' },
+        { name => 'registry', type => 'string', allow_non_utf8 => 1, dead => 1,
+          desc => 'The font registry' },
+        { name => 'encoding', type => 'string', allow_non_utf8 => 1, dead => 1,
+          desc => 'The font encoding' }
+    );
+
+    @outargs = (
+        { name => 'text_layer', type => 'layer',
+          desc => 'The new text layer or -1 if no layer was created.' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (drawable &&
+      (! gimp_pdb_item_is_attached (GIMP_ITEM (drawable), image,
+                                    GIMP_PDB_ITEM_CONTENT, error) ||
+       ! gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error)))
+    success = FALSE;
+
+  if (success)
+    {
+      gchar *real_fontname = g_strdup_printf ("%s %d", family, (gint) size);
+
+      text_layer = text_render (image, drawable, context,
+                                x, y, real_fontname, text,
+                                border, antialias);
+
+      g_free (real_fontname);
+    }
+}
+CODE
+   );
+}
+
+sub text_get_extents {
+    &std_pdb_deprecated ('gimp-text-get-extents-fontname');
+
+    @inargs = (
+        { name => 'text', type => 'string',
+          desc => 'The text to generate (in UTF-8 encoding)' },
+        { name => 'size', type => '0 < float',
+          desc => 'The size of text in either pixels or points' },
+        { name => 'size_type', type => 'enum GimpSizeType', dead => 1,
+          desc => 'The units of specified size' },
+        { name => 'foundry', type => 'string', allow_non_utf8 => 1, dead => 1,
+          desc => 'The font foundry' },
+        { name => 'family', type => 'string', allow_non_utf8 => 1,
+          desc => 'The font family' },
+        { name => 'weight', type => 'string', allow_non_utf8 => 1, dead => 1,
+          desc => 'The font weight' },
+        { name => 'slant', type => 'string', allow_non_utf8 => 1, dead => 1,
+          desc => 'The font slant' },
+        { name => 'set_width', type => 'string', allow_non_utf8 => 1, dead => 1,
+          desc => 'The font set-width' },
+        { name => 'spacing', type => 'string', allow_non_utf8 => 1, dead => 1,
+          desc => 'The font spacing' },
+        { name => 'registry', type => 'string', allow_non_utf8 => 1, dead => 1,
+          desc => 'The font registry' },
+        { name => 'encoding', type => 'string', allow_non_utf8 => 1, dead => 1,
+          desc => 'The font encoding' }
+    );
+
+    @outargs = (
+        { name => 'width', type => 'int32', void_ret => 1,
+          desc => 'The width of the specified font' },
+        { name => 'height', type => 'int32',
+          desc => 'The height of the specified font' },
+        { name => 'ascent', type => 'int32',
+          desc => 'The ascent of the specified font' },
+        { name => 'descent', type => 'int32',
+          desc => 'The descent of the specified font' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  gchar *real_fontname = g_strdup_printf ("%s %d", family, (gint) size);
+
+  success = text_get_extents (real_fontname, text,
+                              &width, &height,
+                              &ascent, &descent);
+
+  g_free (real_fontname);
+}
+CODE
+    );
+}
+
+
+@headers = qw("libgimpbase/gimpbase.h"
+              "text/gimptext-compat.h"
+              "gimppdb-utils.h");
+
+@procs = qw(text_fontname text_get_extents_fontname
+            text text_get_extents);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Text procedures';
+$doc_title = 'gimptexttool';
+$doc_short_desc = 'Functions for controlling the text tool.';
+$doc_long_desc = 'Functions for controlling the text tool.';
+
+1;
diff --git a/pdb/groups/transform_tools.pdb b/pdb/groups/transform_tools.pdb
new file mode 100644
index 0000000..2b212d7
--- /dev/null
+++ b/pdb/groups/transform_tools.pdb
@@ -0,0 +1,532 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub flip {
+    &std_pdb_deprecated ('gimp-item-transform-flip-simple');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+       { name => 'flip_type',
+         type => 'enum GimpOrientationType (no GIMP_ORIENTATION_UNKNOWN)',
+         desc => 'Type of flip' }
+    );
+
+    @outargs = (
+       { name => 'drawable', type => 'drawable', no_declare => 1,
+         desc => 'The flipped drawable' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gint x, y, width, height;
+
+  success = gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                       GIMP_PDB_ITEM_CONTENT |
+                                       GIMP_PDB_ITEM_POSITION, error);
+
+  if (success &&
+      gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
+    {
+      gdouble axis;
+
+      gimp_transform_get_flip_axis (x, y, width, height,
+                                    flip_type, TRUE, &axis);
+
+      if (! gimp_viewable_get_children (GIMP_VIEWABLE (drawable)) &&
+          ! gimp_channel_is_empty (gimp_image_get_mask (gimp_item_get_image (GIMP_ITEM (drawable)))))
+        {
+          if (! gimp_drawable_transform_flip (drawable, context,
+                                              flip_type, axis, FALSE))
+            {
+              success = FALSE;
+            }
+        }
+      else
+        {
+          gimp_item_flip (GIMP_ITEM (drawable), context,
+                          flip_type, axis, FALSE);
+        }
+    }
+}
+CODE
+    );
+}
+
+sub perspective {
+    &std_pdb_deprecated ('gimp-item-transform-perspective');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+       { name => 'interpolation', type => 'boolean',
+         desc => 'Whether to use interpolation' },
+       { name => 'x0', type => 'float',
+         desc => 'The new x coordinate of upper-left corner of original
+                  bounding box' },
+       { name => 'y0', type => 'float',
+         desc => 'The new y coordinate of upper-left corner of original
+                  bounding box' },
+       { name => 'x1', type => 'float',
+         desc => 'The new x coordinate of upper-right corner of original
+                  bounding box' },
+       { name => 'y1', type => 'float',
+         desc => 'The new y coordinate of upper-right corner of original
+                  bounding box' },
+       { name => 'x2', type => 'float',
+         desc => 'The new x coordinate of lower-left corner of original
+                  bounding box' },
+       { name => 'y2', type => 'float',
+         desc => 'The new y coordinate of lower-left corner of original
+                  bounding box' },
+       { name => 'x3', type => 'float',
+         desc => 'The new x coordinate of lower-right corner of original
+                  bounding box' },
+       { name => 'y3', type => 'float',
+         desc => 'The new y coordinate of lower-right corner of original
+                  bounding box' }
+    );
+
+    @outargs = (
+       { name => 'drawable', type => 'drawable', no_declare => 1,
+         desc => 'The newly mapped drawable' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gint x, y, width, height;
+
+  success = gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                       GIMP_PDB_ITEM_CONTENT |
+                                       GIMP_PDB_ITEM_POSITION, error);
+
+  if (success &&
+      gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
+    {
+      GimpMatrix3           matrix;
+      GimpInterpolationType interpolation_type = GIMP_INTERPOLATION_NONE;
+      gint                  off_x, off_y;
+
+      gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y);
+
+      x += off_x;
+      y += off_y;
+
+      /* Assemble the transformation matrix */
+      gimp_matrix3_identity (&matrix);
+      gimp_transform_matrix_perspective (&matrix,
+                                         x, y, width, height,
+                                         x0, y0, x1, y1,
+                                         x2, y2, x3, y3);
+
+      if (interpolation)
+        interpolation_type = gimp->config->interpolation_type;
+
+      if (progress)
+        gimp_progress_start (progress, FALSE, _("Perspective"));
+
+      if (! gimp_viewable_get_children (GIMP_VIEWABLE (drawable)) &&
+          ! gimp_channel_is_empty (gimp_image_get_mask (gimp_item_get_image (GIMP_ITEM (drawable)))))
+        {
+          if (! gimp_drawable_transform_affine (drawable, context,
+                                                &matrix,
+                                                GIMP_TRANSFORM_FORWARD,
+                                                interpolation_type,
+                                                FALSE, progress))
+            {
+              success = FALSE;
+            }
+        }
+      else
+        {
+          gimp_item_transform (GIMP_ITEM (drawable), context, &matrix,
+                               GIMP_TRANSFORM_FORWARD,
+                               interpolation,
+                               FALSE, progress);
+        }
+
+      if (progress)
+        gimp_progress_end (progress);
+    }
+}
+CODE
+    );
+}
+
+sub rotate {
+    &std_pdb_deprecated ('gimp-item-transform-rotate');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+       { name => 'interpolation', type => 'boolean',
+         desc => 'Whether to use interpolation' },
+       { name => 'angle', type => 'float',
+         desc => 'The angle of rotation (radians)' }
+    );
+
+    @outargs = (
+       { name => 'drawable', type => 'drawable', no_declare => 1,
+         desc => 'The rotated drawable' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gint x, y, width, height;
+
+  success = gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                       GIMP_PDB_ITEM_CONTENT |
+                                       GIMP_PDB_ITEM_POSITION, error);
+
+  if (success &&
+      gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
+    {
+      GimpMatrix3           matrix;
+      GimpInterpolationType interpolation_type = GIMP_INTERPOLATION_NONE;
+      gint                  off_x, off_y;
+
+      gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y);
+
+      x += off_x;
+      y += off_y;
+
+      /* Assemble the transformation matrix */
+      gimp_matrix3_identity (&matrix);
+      gimp_transform_matrix_rotate_rect (&matrix,
+                                         x, y, width, height,
+                                         angle);
+
+      if (interpolation)
+        interpolation_type = gimp->config->interpolation_type;
+
+      if (progress)
+        gimp_progress_start (progress, FALSE, _("Rotating"));
+
+      if (! gimp_viewable_get_children (GIMP_VIEWABLE (drawable)) &&
+          ! gimp_channel_is_empty (gimp_image_get_mask (gimp_item_get_image (GIMP_ITEM (drawable)))))
+        {
+          if (! gimp_drawable_transform_affine (drawable, context,
+                                                &matrix,
+                                                GIMP_TRANSFORM_FORWARD,
+                                                interpolation_type,
+                                                FALSE, progress))
+            {
+              success = FALSE;
+            }
+        }
+      else
+        {
+          gimp_item_transform (GIMP_ITEM (drawable), context, &matrix,
+                               GIMP_TRANSFORM_FORWARD,
+                               interpolation,
+                               FALSE, progress);
+        }
+
+      if (progress)
+        gimp_progress_end (progress);
+    }
+}
+CODE
+    );
+}
+
+sub scale {
+    &std_pdb_deprecated ('gimp-item-transform-scale');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+       { name => 'interpolation', type => 'boolean',
+         desc => 'Whether to use interpolation' },
+       { name => 'x0', type => 'float',
+         desc => 'The new x coordinate of the upper-left corner of the
+                   scaled region' },
+       { name => 'y0', type => 'float',
+         desc => 'The new y coordinate of the upper-left corner of the
+                   scaled region' },
+       { name => 'x1', type => 'float',
+         desc => 'The new x coordinate of the lower-right corner of the
+                   scaled region' },
+       { name => 'y1', type => 'float',
+         desc => 'The new y coordinate of the lower-right corner of the
+                   scaled region' }
+    );
+
+    @outargs = (
+       { name => 'drawable', type => 'drawable', no_declare => 1,
+         desc => 'The scaled drawable' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gint x, y, width, height;
+
+  success = (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                        GIMP_PDB_ITEM_CONTENT |
+                                        GIMP_PDB_ITEM_POSITION, error) &&
+             x0 < x1 && y0 < y1);
+
+  if (success &&
+      gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
+    {
+      GimpMatrix3           matrix;
+      GimpInterpolationType interpolation_type = GIMP_INTERPOLATION_NONE;
+      gint                  off_x, off_y;
+
+      gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y);
+
+      x += off_x;
+      y += off_y;
+
+      /* Assemble the transformation matrix */
+      gimp_matrix3_identity (&matrix);
+      gimp_transform_matrix_scale (&matrix,
+                                   x, y, width, height,
+                                   x0, y0, x1 - x0, y1 - y0);
+
+      if (interpolation)
+        interpolation_type = gimp->config->interpolation_type;
+
+      if (progress)
+        gimp_progress_start (progress, FALSE, _("Scaling"));
+
+      if (! gimp_viewable_get_children (GIMP_VIEWABLE (drawable)) &&
+          ! gimp_channel_is_empty (gimp_image_get_mask (gimp_item_get_image (GIMP_ITEM (drawable)))))
+        {
+          if (! gimp_drawable_transform_affine (drawable, context,
+                                                &matrix,
+                                                GIMP_TRANSFORM_FORWARD,
+                                                interpolation_type,
+                                                FALSE, progress))
+            {
+              success = FALSE;
+            }
+        }
+      else
+        {
+          gimp_item_transform (GIMP_ITEM (drawable), context, &matrix,
+                               GIMP_TRANSFORM_FORWARD,
+                               interpolation,
+                               FALSE, progress);
+        }
+
+      if (progress)
+        gimp_progress_end (progress);
+    }
+}
+CODE
+    );
+}
+
+sub shear {
+    &std_pdb_deprecated ('gimp-item-transform-shear');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+       { name => 'interpolation', type => 'boolean',
+         desc => 'Whether to use interpolation' },
+       { name => 'shear_type',
+         type => 'enum GimpOrientationType (no GIMP_ORIENTATION_UNKNOWN)',
+         desc => 'Type of shear' },
+       { name => 'magnitude', type => 'float',
+         desc => 'The magnitude of the shear' }
+    );
+
+    @outargs = (
+       { name => 'drawable', type => 'drawable', no_declare => 1,
+         desc => 'The sheared drawable' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gint x, y, width, height;
+
+  success = gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                       GIMP_PDB_ITEM_CONTENT |
+                                       GIMP_PDB_ITEM_POSITION, error);
+
+  if (success &&
+      gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
+    {
+      GimpMatrix3           matrix;
+      GimpInterpolationType interpolation_type = GIMP_INTERPOLATION_NONE;
+      gint                  off_x, off_y;
+
+      gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y);
+
+      x += off_x;
+      y += off_y;
+
+      /* Assemble the transformation matrix */
+      gimp_matrix3_identity (&matrix);
+      gimp_transform_matrix_shear (&matrix,
+                                   x, y, width, height,
+                                   shear_type, magnitude);
+
+      if (interpolation)
+        interpolation_type = gimp->config->interpolation_type;
+
+      if (progress)
+        gimp_progress_start (progress, FALSE, _("Shearing"));
+
+      if (! gimp_viewable_get_children (GIMP_VIEWABLE (drawable)) &&
+          ! gimp_channel_is_empty (gimp_image_get_mask (gimp_item_get_image (GIMP_ITEM (drawable)))))
+        {
+          if (! gimp_drawable_transform_affine (drawable, context,
+                                                &matrix,
+                                                GIMP_TRANSFORM_FORWARD,
+                                                interpolation_type,
+                                                FALSE, progress))
+            {
+              success = FALSE;
+            }
+        }
+      else
+        {
+          gimp_item_transform (GIMP_ITEM (drawable), context, &matrix,
+                               GIMP_TRANSFORM_FORWARD,
+                               interpolation,
+                               FALSE, progress);
+        }
+
+      if (progress)
+        gimp_progress_end (progress);
+    }
+}
+CODE
+    );
+}
+
+sub transform_2d {
+    &std_pdb_deprecated ('gimp-item-transform-2d');
+
+    @inargs = (
+       { name => 'drawable', type => 'drawable',
+         desc => 'The affected drawable' },
+       { name => 'interpolation', type => 'boolean',
+         desc => 'Whether to use interpolation' },
+       { name => 'source_x', type => 'float',
+         desc => 'X coordinate of the transformation center' },
+       { name => 'source_y', type => 'float',
+         desc => 'Y coordinate of the transformation center' },
+       { name => 'scale_x', type => 'float',
+         desc => 'Amount to scale in x direction' },
+       { name => 'scale_y', type => 'float',
+         desc => 'Amount to scale in y direction' },
+       { name => 'angle', type => 'float',
+         desc => 'The angle of rotation (radians)' },
+       { name => 'dest_x', type => 'float',
+         desc => 'X coordinate of where the centre goes' },
+       { name => 'dest_y', type => 'float',
+         desc => 'Y coordinate of where the centre goes' }
+    );
+
+    @outargs = (
+       { name => 'drawable', type => 'drawable', no_declare => 1,
+         desc => 'The transformed drawable' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  gint x, y, width, height;
+
+  success = gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
+                                       GIMP_PDB_ITEM_CONTENT |
+                                       GIMP_PDB_ITEM_POSITION, error);
+
+  if (success &&
+      gimp_item_mask_intersect (GIMP_ITEM (drawable), &x, &y, &width, &height))
+    {
+      GimpMatrix3           matrix;
+      GimpInterpolationType interpolation_type = GIMP_INTERPOLATION_NONE;
+
+      /* Assemble the transformation matrix */
+      gimp_matrix3_identity  (&matrix);
+      gimp_matrix3_translate (&matrix, -source_x, -source_y);
+      gimp_matrix3_scale     (&matrix, scale_x, scale_y);
+      gimp_matrix3_rotate    (&matrix, angle);
+      gimp_matrix3_translate (&matrix, dest_x, dest_y);
+
+      if (interpolation)
+        interpolation_type = gimp->config->interpolation_type;
+
+      if (progress)
+        gimp_progress_start (progress, FALSE, _("2D Transform"));
+
+      if (! gimp_viewable_get_children (GIMP_VIEWABLE (drawable)) &&
+          ! gimp_channel_is_empty (gimp_image_get_mask (gimp_item_get_image (GIMP_ITEM (drawable)))))
+        {
+          if (! gimp_drawable_transform_affine (drawable, context,
+                                                &matrix, GIMP_TRANSFORM_FORWARD,
+                                                interpolation_type,
+                                                FALSE, progress))
+            {
+              success = FALSE;
+            }
+        }
+      else
+        {
+          gimp_item_transform (GIMP_ITEM (drawable), context, &matrix,
+                               GIMP_TRANSFORM_FORWARD,
+                               interpolation,
+                               FALSE, progress);
+        }
+
+      if (progress)
+        gimp_progress_end (progress);
+    }
+}
+CODE
+    );
+}
+
+
+@headers = qw("libgimpmath/gimpmath.h"
+              "config/gimpcoreconfig.h"
+              "core/gimp.h"
+              "core/gimpchannel.h"
+              "core/gimp-transform-utils.h"
+              "core/gimpimage.h"
+              "core/gimpdrawable.h"
+              "core/gimpdrawable-transform.h"
+              "core/gimpprogress.h"
+              "gimppdb-utils.h"
+              "gimp-intl.h");
+
+@procs = qw(flip
+            perspective
+            rotate
+            scale
+            shear
+            transform_2d);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Transform Tool procedures';
+$doc_title = 'gimptransformtools';
+$doc_short_desc = 'Access to toolbox transform tools.';
+$doc_long_desc = 'Functions giving access to toolbox transform tools.';
+
+1;
diff --git a/pdb/groups/unit.pdb b/pdb/groups/unit.pdb
new file mode 100644
index 0000000..31bd434
--- /dev/null
+++ b/pdb/groups/unit.pdb
@@ -0,0 +1,376 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# "Perlized" from C source by Manish Singh <yosh gimp org>
+
+sub unit_get_number_of_units {
+    $blurb = 'Returns the number of units.';
+
+    $help = 'This procedure returns the number of defined units.';
+
+    &mitch_pdb_misc('1999');
+
+    @outargs = (
+       { name => 'num_units', type => 'int32', libdef => 'GIMP_UNIT_END',
+         desc => 'The number of units', wrap => 1 }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  num_units = _gimp_unit_get_number_of_units (gimp);
+}
+CODE
+    );
+}
+
+sub unit_get_number_of_built_in_units {
+    $blurb = 'Returns the number of built-in units.';
+
+    $help = <<'HELP';
+This procedure returns the number of defined units built-in to GIMP.
+HELP
+
+    &mitch_pdb_misc('1999');
+
+    @outargs = (
+       { name => 'num_units', type => 'int32', libdef => 'GIMP_UNIT_END',
+         desc => 'The number of built-in units', wrap => 1 }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  num_units = _gimp_unit_get_number_of_built_in_units (gimp);
+}
+CODE
+    );
+}
+
+sub unit_new {
+    $blurb = "Creates a new unit and returns it's integer ID.";
+
+    $help = <<'HELP';
+This procedure creates a new unit and returns it's integer ID. Note that the
+new unit will have it's deletion flag set to TRUE, so you will have to set it
+to FALSE with gimp_unit_set_deletion_flag() to make it persistent.
+HELP
+
+    &mitch_pdb_misc('1999');
+
+    @inargs = (
+       { name => 'identifier', type => 'string', wrap => 1, non_empty => 1,
+         desc => "The new unit's identifier" },
+       { name => 'factor', type => 'float',
+         desc => "The new unit's factor" },
+       { name => 'digits', type => 'int32',
+         desc => "The new unit's digits" },
+       { name => 'symbol', type => 'string', non_empty => 1,
+         desc => "The new unit's symbol" },
+       { name => 'abbreviation', type => 'string', non_empty => 1,
+         desc => "The new unit's abbreviation" },
+       { name => 'singular', type => 'string', non_empty => 1,
+         desc => "The new unit's singular form" },
+       { name => 'plural', type => 'string', non_empty => 1,
+         desc => "The new unit's plural form" }
+    );
+
+    @outargs = (
+       { name => 'unit_id', type => 'unit',
+         desc => "The new unit's ID",
+         wrap => 1, libdef => 'GIMP_UNIT_INCH' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  unit_id = _gimp_unit_new (gimp, identifier, factor, digits,
+                            symbol, abbreviation, singular, plural);
+}
+CODE
+    );
+}
+
+sub unit_get_deletion_flag {
+    $blurb = "Returns the deletion flag of the unit.";
+
+    $help = <<'HELP';
+This procedure returns the deletion flag of the unit. If this value is TRUE the
+unit's definition will not be saved in the user's unitrc file on gimp exit.
+HELP
+
+    &mitch_pdb_misc('1999');
+
+    @inargs = (
+       { name => 'unit_id', type => 'unit',
+         desc => "The unit's integer ID", wrap => 1 }
+    );
+
+    @outargs = (
+       { name => 'deletion_flag', type => 'boolean',
+         desc => "The unit's deletion flag" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  deletion_flag = _gimp_unit_get_deletion_flag (gimp, unit_id);
+}
+CODE
+    );
+}
+
+sub unit_set_deletion_flag {
+    $blurb = 'Sets the deletion flag of a unit.';
+
+    $help = <<'HELP';
+This procedure sets the unit's deletion flag. If the deletion flag of a unit is
+TRUE on gimp exit, this unit's definition will not be saved in the user's
+unitrc.
+HELP
+
+    &mitch_pdb_misc('1999');
+
+    @inargs = (
+       { name => 'unit_id', type => 'unit',
+         desc => "The unit's integer ID", wrap => 1 },
+       { name => 'deletion_flag', type => 'boolean',
+         desc => 'The new deletion flag of the unit' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  _gimp_unit_set_deletion_flag (gimp, unit_id, deletion_flag);
+}
+CODE
+    );
+}
+
+sub unit_get_identifier {
+    $blurb = "Returns the textual identifier of the unit.";
+
+    $help = <<'HELP';
+This procedure returns the textual identifier of the unit. For built-in units
+it will be the english singular form of the unit's name. For user-defined units
+this should equal to the singular form.
+HELP
+
+    &mitch_pdb_misc('1999');
+
+    @inargs = (
+       { name => 'unit_id', type => 'unit',
+         desc => "The unit's integer ID", wrap => 1 }
+    );
+
+    @outargs = (
+       { name => 'identifier', type => 'string',
+         desc => "The unit's textual identifier" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  identifier = g_strdup (_gimp_unit_get_identifier (gimp, unit_id));
+}
+CODE
+    );
+}
+
+sub unit_get_factor {
+    $blurb = "Returns the factor of the unit.";
+
+    $help = <<'HELP';
+This procedure returns the unit's factor which indicates how many units make up
+an inch. Note that asking for the factor of "pixels" will produce an error.
+HELP
+
+    &mitch_pdb_misc('1999');
+
+    @inargs = (
+       { name => 'unit_id', type => 'unit',
+         desc => "The unit's integer ID", wrap => 1 }
+    );
+
+    @outargs = (
+       { name => 'factor', type => 'float',
+         desc => "The unit's factor" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  factor = _gimp_unit_get_factor (gimp, unit_id);
+}
+CODE
+    );
+}
+
+sub unit_get_digits {
+    $blurb = "Returns the number of digits of the unit.";
+
+    $help = <<'HELP';
+This procedure returns the number of digits you should provide in input or
+output functions to get approximately the same accuracy as with two digits and
+inches. Note that asking for the digits of "pixels" will produce an error.
+HELP
+
+    &mitch_pdb_misc('1999');
+
+    @inargs = (
+       { name => 'unit_id', type => 'unit',
+         desc => "The unit's integer ID", wrap => 1 }
+    );
+
+    @outargs = (
+       { name => 'digits', type => 'int32',
+         desc => "The unit's number of digits" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  digits = _gimp_unit_get_digits (gimp, unit_id);
+}
+CODE
+    );
+}
+
+sub unit_get_symbol {
+    $blurb = "Returns the symbol of the unit.";
+
+    $help = <<'HELP';
+This procedure returns the symbol of the unit ("''" for inches).
+HELP
+
+    &mitch_pdb_misc('1999');
+
+    @inargs = (
+       { name => 'unit_id', type => 'unit',
+         desc => "The unit's integer ID", wrap => 1 }
+    );
+
+    @outargs = (
+       { name => 'symbol', type => 'string',
+         desc => "The unit's symbol" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  symbol = g_strdup (_gimp_unit_get_symbol (gimp, unit_id));
+}
+CODE
+    );
+}
+
+sub unit_get_abbreviation {
+    $blurb = "Returns the abbreviation of the unit.";
+
+    $help = <<'HELP';
+This procedure returns the abbreviation of the unit ("in" for inches).
+HELP
+
+    &mitch_pdb_misc('1999');
+
+    @inargs = (
+       { name => 'unit_id', type => 'unit',
+         desc => "The unit's integer ID", wrap => 1 }
+    );
+
+    @outargs = (
+       { name => 'abbreviation', type => 'string',
+         desc => "The unit's abbreviation" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  abbreviation = g_strdup (_gimp_unit_get_abbreviation (gimp, unit_id));
+}
+CODE
+    );
+}
+
+sub unit_get_singular {
+    $blurb = "Returns the singular form of the unit.";
+    $help  = 'This procedure returns the singular form of the unit.';
+
+    &mitch_pdb_misc('1999');
+
+    @inargs = (
+       { name => 'unit_id', type => 'unit',
+         desc => "The unit's integer ID", wrap => 1 }
+    );
+
+    @outargs = (
+       { name => 'singular', type => 'string',
+         desc => "The unit's singular form" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  singular = g_strdup (_gimp_unit_get_singular (gimp, unit_id));
+}
+CODE
+    );
+}
+
+sub unit_get_plural {
+    $blurb = "Returns the plural form of the unit.";
+    $help  = 'This procedure returns the plural form of the unit.';
+
+    &mitch_pdb_misc('1999');
+
+    @inargs = (
+       { name => 'unit_id', type => 'unit',
+         desc => "The unit's integer ID", wrap => 1 }
+    );
+
+    @outargs = (
+       { name => 'plural', type => 'string',
+         desc => "The unit's plural form" }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  plural = g_strdup (_gimp_unit_get_plural (gimp, unit_id));
+}
+CODE
+    );
+}
+
+
+@headers = qw("libgimpbase/gimpbase.h"
+              "core/gimpunit.h");
+
+@procs = qw(unit_get_number_of_units unit_get_number_of_built_in_units
+           unit_new unit_get_deletion_flag unit_set_deletion_flag
+            unit_get_identifier unit_get_factor unit_get_digits
+            unit_get_symbol unit_get_abbreviation unit_get_singular
+            unit_get_plural);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Units';
+$doc_title = 'gimpunit';
+$doc_short_desc = 'Provides a collection of predefined units and functions for creating user-defined units.';
+$doc_long_desc = 'Provides a collection of predefined units and functions for creating user-defined units.';
+$doc_see_also = '#GimpUnitMenu, #GimpSizeEntry';
+
+1;
diff --git a/pdb/groups/vectors.pdb b/pdb/groups/vectors.pdb
new file mode 100644
index 0000000..134c1b1
--- /dev/null
+++ b/pdb/groups/vectors.pdb
@@ -0,0 +1,1365 @@
+# GIMP - The GNU Image Manipulation Program
+# Copyright (C) 1995 Spencer Kimball and Peter Mattis
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+sub vectors_new {
+    $blurb = 'Creates a new empty vectors object.';
+
+    $help = <<'HELP';
+Creates a new empty vectors object. The vectors object needs to be added to
+the image using gimp_image_insert_vectors().
+HELP
+
+    &simon_pdb_misc('2005', '2.4');
+
+    @inargs = (
+       { name => 'image', type => 'image',
+         desc => 'The image' },
+        { name => 'name', type => 'string',
+          desc => 'the name of the new vector object.' }
+    );
+
+    @outargs = (
+        { name => 'vectors', type => 'vectors',
+          desc => 'the current vector object, 0 if no vector exists
+                   in the image.' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  vectors = gimp_vectors_new (image, name);
+}
+CODE
+    );
+}
+
+sub vectors_new_from_text_layer {
+    $blurb = 'Creates a new vectors object from a text layer.';
+
+    $help = <<'HELP';
+Creates a new vectors object from a text layer. The vectors object needs to
+be added to the image using gimp_image_insert_vectors().
+HELP
+
+    &marcus_pdb_misc('2008', '2.6');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+         desc => 'The image.' },
+        { name => 'layer', type => 'layer',
+         desc => 'The text layer.' }
+    );
+
+    @outargs = (
+        { name => 'vectors', type => 'vectors',
+         desc => 'The vectors of the text layer.' }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_layer_is_text_layer (layer, 0, error))
+    {
+      gint x, y;
+
+      vectors = gimp_text_vectors_new (image,
+                                      gimp_text_layer_get_text (GIMP_TEXT_LAYER (layer)));
+
+      gimp_item_get_offset (GIMP_ITEM (layer), &x, &y);
+      gimp_item_translate (GIMP_ITEM (vectors), x, y, FALSE);
+    }
+  else
+    {
+      success = FALSE;
+    }
+}
+CODE
+    );
+}
+
+sub vectors_copy {
+    $blurb = 'Copy a vectors object.';
+
+    $help = <<'HELP';
+This procedure copies the specified vectors object and returns the copy.
+HELP
+
+    barak_pdb_misc('2008', '2.6');
+
+    @inargs = (
+       { name => 'vectors', type => 'vectors',
+         desc => 'The vectors object to copy' }
+    );
+
+    @outargs = (
+       { name => 'vectors_copy', type => 'vectors',
+         desc => 'The newly copied vectors object' }
+    );
+
+    %invoke = (
+       code => <<'CODE'
+{
+  vectors_copy = GIMP_VECTORS (gimp_item_duplicate (GIMP_ITEM (vectors),
+                               G_TYPE_FROM_INSTANCE (vectors)));
+
+  if (! vectors_copy)
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub vectors_get_strokes {
+    $blurb = 'List the strokes associated with the passed path.';
+
+    $help = <<'HELP';
+Returns an Array with the stroke-IDs associated with the passed path.
+HELP
+
+    &simon_pdb_misc('2005', '2.4');
+
+    @inargs = (
+       { name => 'vectors', type => 'vectors',
+         desc => 'The vectors object' }
+    );
+
+    @outargs = (
+        { name => 'stroke_ids', type => 'int32array',
+          desc => 'List of the strokes belonging to the path.',
+          array => { name => 'num_strokes',
+                     desc => 'The number of strokes returned.' } }
+    );
+
+    %invoke = (
+        code => <<"CODE"
+{
+  num_strokes = gimp_vectors_get_n_strokes (vectors);
+
+  if (num_strokes)
+    {
+      GimpStroke *cur_stroke;
+      gint        i = 0;
+
+      stroke_ids = g_new (gint32, num_strokes);
+
+      for (cur_stroke = gimp_vectors_stroke_get_next (vectors, NULL);
+           cur_stroke;
+           cur_stroke = gimp_vectors_stroke_get_next (vectors, cur_stroke))
+        {
+          stroke_ids[i] = gimp_stroke_get_ID (cur_stroke);
+          i++;
+        }
+    }
+}
+CODE
+    );
+}
+
+sub vectors_stroke_get_length {
+    $blurb = 'Measure the length of the given stroke.';
+    $help  = 'Measure the length of the given stroke.';
+
+    &simon_pdb_misc('2005', '2.4');
+
+    @inargs = (
+       { name => 'vectors', type => 'vectors',
+         desc => 'The vectors object' },
+       { name => 'stroke_id', type => 'int32',
+         desc => 'The stroke ID' },
+        { name => 'precision', type => 'float',
+          desc => 'The precision used for the approximation' }
+    );
+
+    @outargs = (
+        { name => 'length', type => 'float',
+          desc => 'The length (in pixels) of the given stroke.' }
+    );
+
+    %invoke = (
+        code => <<"CODE"
+{
+  GimpStroke *stroke = gimp_pdb_get_vectors_stroke (vectors, stroke_id, 0, error);
+  
+  if (stroke)
+    length = gimp_stroke_get_length (stroke, precision);
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub vectors_stroke_get_point_at_dist {
+    $blurb = 'Get point at a specified distance along the stroke.';
+
+    $help = <<'HELP';
+This will return the x,y position of a point at a given distance along the
+stroke. The distance will be obtained by first digitizing the 
+curve internally and then walking along the curve. For a closed stroke the
+start of the path is the first point on the path that was created. This might
+not be obvious. If the stroke is not long enough, a "valid" flag will be FALSE.
+HELP
+
+    &simon_pdb_misc('2005', '2.4');
+
+    @inargs = (
+       { name => 'vectors', type => 'vectors',
+         desc => 'The vectors object' },
+       { name => 'stroke_id', type => 'int32',
+         desc => 'The stroke ID' },
+        { name => 'dist', type => 'float',
+          desc => 'The given distance.' },
+        { name => 'precision', type => 'float',
+          desc => 'The precision used for the approximation' }
+    );
+
+    @outargs = (
+        { name => 'x_point', type => 'float', void_ret => 1,
+          desc => 'The x position of the point.' },
+        { name => 'y_point', type => 'float',
+          desc => 'The y position of the point.' },
+        { name => 'slope', type => 'float',
+          desc => 'The slope (dy / dx) at the specified point.' },
+        { name => 'valid', type => 'boolean',
+          desc => 'Indicator for the validity of the returned data.' }
+    );
+
+    %invoke = (
+        code => <<"CODE"
+{
+  GimpStroke *stroke = gimp_pdb_get_vectors_stroke (vectors, stroke_id, 0, error);
+  
+  if (stroke)
+    {
+      GimpCoords coord;
+
+      valid = gimp_stroke_get_point_at_dist (stroke, dist, precision,
+                                             &coord, &slope);
+      x_point = valid ? coord.x : 0;
+      y_point = valid ? coord.y : 0;
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub vectors_remove_stroke {
+    $blurb = 'remove the stroke from a vectors object.';
+
+    $help = <<'HELP';
+Remove the stroke from a vectors object.
+HELP
+
+    &simon_pdb_misc('2005', '2.4');
+
+    @inargs = (
+       { name => 'vectors', type => 'vectors',
+         desc => 'The vectors object' },
+       { name => 'stroke_id', type => 'int32',
+         desc => 'The stroke ID' }
+    );
+
+    %invoke = (
+        code => <<"CODE"
+{
+  GimpStroke *stroke = gimp_pdb_get_vectors_stroke (vectors, stroke_id,
+                                                    GIMP_PDB_ITEM_CONTENT, error);
+  
+  if (stroke)
+    {
+      if (gimp_item_is_attached (GIMP_ITEM (vectors)))
+        gimp_image_undo_push_vectors_mod (gimp_item_get_image (GIMP_ITEM (vectors)),
+                                          _("Remove path stroke"),
+                                          vectors);
+
+      gimp_vectors_stroke_remove (vectors, stroke);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub vectors_stroke_close {
+    $blurb = 'closes the specified stroke.';
+
+    $help = <<'HELP';
+Closes the specified stroke.
+HELP
+
+    &simon_pdb_misc('2005', '2.4');
+
+    @inargs = (
+       { name => 'vectors', type => 'vectors',
+         desc => 'The vectors object' },
+       { name => 'stroke_id', type => 'int32',
+         desc => 'The stroke ID' }
+    );
+
+    %invoke = (
+        code => <<"CODE"
+{
+  GimpStroke *stroke = gimp_pdb_get_vectors_stroke (vectors, stroke_id,
+                                                    GIMP_PDB_ITEM_CONTENT, error);
+  
+  if (stroke)
+    {
+      if (gimp_item_is_attached (GIMP_ITEM (vectors)))
+        gimp_image_undo_push_vectors_mod (gimp_item_get_image (GIMP_ITEM (vectors)),
+                                          _("Close path stroke"),
+                                          vectors);
+
+      gimp_vectors_freeze (vectors);
+      gimp_stroke_close (stroke);
+      gimp_vectors_thaw (vectors);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+sub vectors_stroke_translate {
+    $blurb = 'translate the given stroke.';
+
+    $help = <<'HELP';
+Translate the given stroke.
+HELP
+
+    &simon_pdb_misc('2005', '2.4');
+
+    @inargs = (
+       { name => 'vectors', type => 'vectors',
+         desc => 'The vectors object' },
+       { name => 'stroke_id', type => 'int32',
+         desc => 'The stroke ID' },
+       { name => "off_x", type => 'int32',
+          desc => "Offset in x direction" },
+       { name => "off_y", type => 'int32',
+          desc => "Offset in y direction" }
+    );
+
+    %invoke = (
+        code => <<"CODE"
+{
+  GimpStroke *stroke = gimp_pdb_get_vectors_stroke (vectors, stroke_id,
+                                                    GIMP_PDB_ITEM_CONTENT |
+                                                    GIMP_PDB_ITEM_POSITION,
+                                                    error);
+  
+  if (stroke)
+    {
+      if (gimp_item_is_attached (GIMP_ITEM (vectors)))
+        gimp_image_undo_push_vectors_mod (gimp_item_get_image (GIMP_ITEM (vectors)),
+                                          _("Translate path stroke"),
+                                          vectors);
+
+      gimp_vectors_freeze (vectors);
+      gimp_stroke_translate (stroke, off_x, off_y);
+      gimp_vectors_thaw (vectors);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub vectors_stroke_scale {
+    $blurb = 'scales the given stroke.';
+
+    $help = <<'HELP';
+Scale the given stroke.
+HELP
+
+    &simon_pdb_misc('2005', '2.4');
+
+    @inargs = (
+       { name => 'vectors', type => 'vectors',
+         desc => 'The vectors object' },
+       { name => 'stroke_id', type => 'int32',
+         desc => 'The stroke ID' },
+       { name => "scale_x", type => 'float',
+         desc => "Scale factor in x direction" },
+       { name => "scale_y", type => 'float',
+         desc => "Scale factor in y direction" }
+    );
+
+    %invoke = (
+        code => <<"CODE"
+{
+  GimpStroke *stroke = gimp_pdb_get_vectors_stroke (vectors, stroke_id,
+                                                    GIMP_PDB_ITEM_CONTENT |
+                                                    GIMP_PDB_ITEM_POSITION,
+                                                    error);
+  
+  if (stroke)
+    {
+      if (gimp_item_is_attached (GIMP_ITEM (vectors)))
+        gimp_image_undo_push_vectors_mod (gimp_item_get_image (GIMP_ITEM (vectors)),
+                                          _("Scale path stroke"),
+                                          vectors);
+
+      gimp_vectors_freeze (vectors);
+      gimp_stroke_scale (stroke, scale_x, scale_y);
+      gimp_vectors_thaw (vectors);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub vectors_stroke_rotate {
+    $blurb = 'rotates the given stroke.';
+
+    $help = <<'HELP';
+Rotates the given stroke around given center by angle (in degrees).
+HELP
+
+    &joao_pdb_misc('2006', '2.4');
+    @inargs = (
+       { name => 'vectors', type => 'vectors',
+         desc => 'The vectors object' },
+       { name => 'stroke_id', type => 'int32',
+         desc => 'The stroke ID' },
+       { name => "center_x", type => 'float',
+         desc => "X coordinate of the rotation center" },
+       { name => "center_y", type => 'float',
+         desc => "Y coordinate of the rotation center" },
+        { name => "angle", type => 'float',
+         desc => "angle to rotate about" }
+    );
+
+    %invoke = (
+        code => <<"CODE"
+{
+  GimpStroke *stroke = gimp_pdb_get_vectors_stroke (vectors, stroke_id,
+                                                    GIMP_PDB_ITEM_CONTENT |
+                                                    GIMP_PDB_ITEM_POSITION,
+                                                    error);
+  
+  if (stroke)
+    {
+      if (gimp_item_is_attached (GIMP_ITEM (vectors)))
+        gimp_image_undo_push_vectors_mod (gimp_item_get_image (GIMP_ITEM (vectors)),
+                                          _("Rotate path stroke"),
+                                          vectors);
+
+      gimp_vectors_freeze (vectors);
+      gimp_stroke_rotate (stroke, center_x, center_y, angle);
+      gimp_vectors_thaw (vectors);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub vectors_stroke_flip {
+    $blurb = 'flips the given stroke.';
+
+    $help = <<'HELP';
+Rotates the given stroke around given center by angle (in degrees).
+HELP
+
+    &joao_pdb_misc('2006', '2.4');
+    @inargs = (
+       { name => 'vectors', type => 'vectors',
+         desc => 'The vectors object' },
+       { name => 'stroke_id', type => 'int32',
+         desc => 'The stroke ID' },
+       { name => "flip_type",
+         type => 'enum GimpOrientationType (no GIMP_ORIENTATION_UNKNOWN)',
+         desc => "Flip orientation, either vertical or horizontal" },
+       { name => "axis", type => 'float',
+         desc => "axis coordinate about which to flip, in pixels" }
+    );
+
+    %invoke = (
+        code => <<"CODE"
+{
+  GimpStroke *stroke = gimp_pdb_get_vectors_stroke (vectors, stroke_id,
+                                                    GIMP_PDB_ITEM_CONTENT |
+                                                    GIMP_PDB_ITEM_POSITION,
+                                                    error);
+  
+  if (stroke)
+    {
+      if (gimp_item_is_attached (GIMP_ITEM (vectors)))
+        gimp_image_undo_push_vectors_mod (gimp_item_get_image (GIMP_ITEM (vectors)),
+                                          _("Flip path stroke"),
+                                          vectors);
+
+      gimp_vectors_freeze (vectors);
+      gimp_stroke_flip (stroke, flip_type, axis);
+      gimp_vectors_thaw (vectors);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub vectors_stroke_flip_free {
+    $blurb = 'flips the given stroke about an arbitrary axis.';
+
+    $help = <<'HELP';
+Flips the given stroke about an arbitrary axis. Axis is defined by two coordinates
+in the image (in pixels), through which the flipping axis passes.
+HELP
+
+    &joao_pdb_misc('2006', '2.4');
+    @inargs = (
+       { name => 'vectors', type => 'vectors',
+         desc => 'The vectors object' },
+       { name => 'stroke_id', type => 'int32',
+         desc => 'The stroke ID' },
+       { name => "x1", type => 'float',
+         desc => "X coordinate of the first point of the flipping axis" },
+       { name => "y1", type => 'float',
+         desc => "Y coordinate of the first point of the flipping axis" },
+       { name => "x2", type => 'float',
+         desc => "X coordinate of the second point of the flipping axis" },
+       { name => "y2", type => 'float',
+         desc => "Y coordinate of the second point of the flipping axis" },
+
+    );
+
+    %invoke = (
+        code => <<"CODE"
+{
+  GimpStroke *stroke = gimp_pdb_get_vectors_stroke (vectors, stroke_id,
+                                                    GIMP_PDB_ITEM_CONTENT |
+                                                    GIMP_PDB_ITEM_POSITION,
+                                                    error);
+  
+  if (stroke)
+    {
+      if (gimp_item_is_attached (GIMP_ITEM (vectors)))
+        gimp_image_undo_push_vectors_mod (gimp_item_get_image (GIMP_ITEM (vectors)),
+                                          _("Flip path stroke"),
+                                          vectors);
+
+      gimp_vectors_freeze (vectors);
+      gimp_stroke_flip_free (stroke, x1, y1, x2, y2);
+      gimp_vectors_thaw (vectors);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub vectors_stroke_get_points {
+    $blurb = 'returns the control points of a stroke.';
+
+    $help = <<'HELP';
+returns the control points of a stroke. The interpretation of the coordinates
+returned depends on the type of the stroke. For Gimp 2.4 this is always a
+bezier stroke, where the coordinates are the control points.
+HELP
+
+    &simon_pdb_misc('2006', '2.4');
+
+    @inargs = (
+       { name => 'vectors', type => 'vectors',
+         desc => 'The vectors object' },
+       { name => 'stroke_id', type => 'int32',
+         desc => 'The stroke ID' }
+    );
+
+    @outargs = (
+        { name => 'type', type => 'enum GimpVectorsStrokeType',
+          desc => 'type of the stroke (always GIMP_VECTORS_STROKE_TYPE_BEZIER for now).' },
+        { name => 'controlpoints', type => 'floatarray',
+          desc => 'List of the control points for the stroke (x0, y0, x1, y1, ...).',
+          array => { name => 'num_points',
+                     desc => 'The number of floats returned.' } },
+        { name => 'closed', type => 'boolean',
+          desc => 'Whether the stroke is closed or not.' }
+    );
+
+    %invoke = (
+        code => <<"CODE"
+{
+  GimpStroke *stroke = gimp_pdb_get_vectors_stroke (vectors, stroke_id, 0, error);
+  
+  if (GIMP_IS_BEZIER_STROKE (stroke))
+    {
+      GArray *points_array;
+      gint    i;
+
+      points_array = gimp_stroke_control_points_get (stroke, &closed);
+
+      if (points_array)
+        {
+          num_points = points_array->len;
+          controlpoints = g_new (gdouble, num_points * 2);
+
+          type = GIMP_VECTORS_STROKE_TYPE_BEZIER;
+          for (i = 0; i < num_points; i++)
+            {
+              controlpoints[2*i]   = g_array_index (points_array,
+                                                   GimpAnchor, i).position.x;
+              controlpoints[2*i+1] = g_array_index (points_array,
+                                                   GimpAnchor, i).position.y;
+            }
+          g_array_free (points_array, TRUE);
+          num_points *= 2;
+        }
+      else
+        success = FALSE;
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+sub vectors_stroke_interpolate {
+    $blurb = 'returns polygonal approximation of the stroke.';
+
+    $help = <<'HELP';
+returns polygonal approximation of the stroke.
+HELP
+
+    &simon_pdb_misc('2005', '2.4');
+
+    @inargs = (
+       { name => 'vectors', type => 'vectors',
+         desc => 'The vectors object' },
+       { name => 'stroke_id', type => 'int32',
+         desc => 'The stroke ID' },
+        { name => 'precision', type => 'float',
+          desc => 'The precision used for the approximation' }
+    );
+
+    @outargs = (
+        { name => 'coords', type => 'floatarray',
+          desc => 'List of the coords along the path (x0, y0, x1, y1, ...).',
+          array => { name => 'num_coords',
+                     desc => 'The number of floats returned.' } },
+        { name => 'closed', type => 'boolean',
+          desc => 'Whether the stroke is closed or not.' }
+    );
+
+    %invoke = (
+        code => <<"CODE"
+{
+  GimpStroke *stroke = gimp_pdb_get_vectors_stroke (vectors, stroke_id, 0, error);
+  
+  if (stroke)
+    {
+      GArray *coords_array;
+      gint    i;
+
+      coords_array = gimp_stroke_interpolate (stroke, precision, &closed);
+
+      if (coords_array)
+        {
+          num_coords = coords_array->len;
+          coords = g_new (gdouble, num_coords * 2);
+
+          for (i = 0; i < num_coords; i++)
+            {
+              coords[2*i]   = g_array_index (coords_array, GimpCoords, i).x;
+              coords[2*i+1] = g_array_index (coords_array, GimpCoords, i).y;
+            }
+          g_array_free (coords_array, TRUE);
+          num_coords *= 2;
+        }
+      else
+        success = FALSE;
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+
+sub vectors_stroke_new_from_points {
+    $blurb = 'Adds a stroke of a given type to the vectors object.';
+
+    $help = <<'HELP';
+Adds a stroke of a given type to the vectors object. The coordinates of the
+control points can be specified.
+For now only strokes of the type GIMP_VECTORS_STROKE_TYPE_BEZIER are supported.
+The control points are specified as a pair of float values for the x- and
+y-coordinate.
+The Bezier stroke type needs a multiple of three control points. Each Bezier
+segment endpoint (anchor, A) has two additional control points (C) associated.
+They are specified in the order CACCACCAC...
+HELP
+
+    &simon_pdb_misc('2006', '2.4');
+
+    @inargs = (
+       { name => 'vectors', type => 'vectors',
+         desc => 'The vectors object' },
+       { name => 'type', type => 'enum GimpVectorsStrokeType',
+         desc => 'type of the stroke (always GIMP_VECTORS_STROKE_TYPE_BEZIER for now).' },
+       { name => 'controlpoints', type => 'floatarray',
+         desc => 'List of the x- and y-coordinates of the control points.',
+          array => { name => 'num_points',
+                     desc => 'The number of elements in the array, i.e. the
+                              number of controlpoints in the stroke * 2
+                              (x- and y-coordinate).' } },
+        { name => 'closed', type => 'boolean',
+          desc => 'Whether the stroke is to be closed or not.' }
+    );
+
+    @outargs = (
+        { name => 'stroke_id', type => 'int32',
+          desc => 'The stroke ID of the newly created stroke.' }
+    );
+
+    %invoke = (
+        code => <<"CODE"
+{
+  GimpStroke *stroke;
+  GimpCoords *coords;
+  GimpCoords  default_coords = GIMP_COORDS_DEFAULT_VALUES;
+  gint i;
+
+  success = FALSE;
+
+  if (type == GIMP_VECTORS_STROKE_TYPE_BEZIER &&
+      num_points % 6 == 0)
+    {
+      coords = g_new (GimpCoords, num_points/2);
+      for (i = 0; i < num_points/2; i++)
+        {
+         coords[i] = default_coords;
+         coords[i].x = controlpoints[i*2];
+         coords[i].y = controlpoints[i*2+1];
+       }
+
+      stroke = gimp_stroke_new_from_coords (type, coords, num_points/2, closed);
+      if (stroke)
+        {
+          if (gimp_item_is_attached (GIMP_ITEM (vectors)))
+            gimp_image_undo_push_vectors_mod (gimp_item_get_image (GIMP_ITEM (vectors)),
+                                              _("Add path stroke"),
+                                              vectors);
+
+         gimp_vectors_stroke_add (vectors, stroke);
+         g_object_unref (stroke);
+
+         stroke_id = gimp_stroke_get_ID (stroke);
+
+         success = TRUE;
+       }
+
+      g_free (coords);
+    }
+}
+CODE
+    );
+}
+
+sub vectors_bezier_stroke_new_moveto {
+    $blurb = 'Adds a bezier stroke with a single moveto to the vectors object.';
+
+    $help = <<'HELP';
+Adds a bezier stroke with a single moveto to the vectors object.
+HELP
+
+    &simon_pdb_misc('2005', '2.4');
+
+    @inargs = (
+       { name => 'vectors', type => 'vectors',
+         desc => 'The vectors object' },
+        { name => 'x0', type => 'float',
+          desc => 'The x-coordinate of the moveto' },
+        { name => 'y0', type => 'float',
+          desc => 'The y-coordinate of the moveto' }
+    );
+
+    @outargs = (
+        { name => 'stroke_id', type => 'int32',
+          desc => 'The resulting stroke' }
+    );
+
+    %invoke = (
+        code => <<"CODE"
+{
+  if (gimp_pdb_item_is_modifyable (GIMP_ITEM (vectors),
+                                   GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (vectors), error))
+    {
+      GimpStroke *stroke;
+      GimpCoords  coord0 = GIMP_COORDS_DEFAULT_VALUES;
+
+      coord0.x = x0;
+      coord0.y = y0;
+
+      stroke = gimp_bezier_stroke_new_moveto (&coord0);
+
+      if (gimp_item_is_attached (GIMP_ITEM (vectors)))
+        gimp_image_undo_push_vectors_mod (gimp_item_get_image (GIMP_ITEM (vectors)),
+                                          _("Add path stroke"),
+                                          vectors);
+
+      gimp_vectors_stroke_add (vectors, stroke);
+      g_object_unref (stroke);
+
+      stroke_id = gimp_stroke_get_ID (stroke);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub vectors_bezier_stroke_lineto {
+    $blurb = 'Extends a bezier stroke with a lineto.';
+
+    $help = <<'HELP';
+Extends a bezier stroke with a lineto.
+HELP
+
+    &simon_pdb_misc('2005', '2.4');
+
+    @inargs = (
+       { name => 'vectors', type => 'vectors',
+         desc => 'The vectors object' },
+       { name => 'stroke_id', type => 'int32',
+         desc => 'The stroke ID' },
+        { name => 'x0', type => 'float',
+          desc => 'The x-coordinate of the lineto' },
+        { name => 'y0', type => 'float',
+          desc => 'The y-coordinate of the lineto' }
+    );
+
+    %invoke = (
+        code => <<"CODE"
+{
+  GimpStroke *stroke = gimp_pdb_get_vectors_stroke (vectors, stroke_id,
+                                                    GIMP_PDB_ITEM_CONTENT, error);
+
+  if (stroke)
+    {
+      GimpCoords coord0 = GIMP_COORDS_DEFAULT_VALUES;
+
+      coord0.x = x0;
+      coord0.y = y0;
+
+     if (gimp_item_is_attached (GIMP_ITEM (vectors)))
+       gimp_image_undo_push_vectors_mod (gimp_item_get_image (GIMP_ITEM (vectors)),
+                                         _("Extend path stroke"),
+                                         vectors);
+
+      gimp_vectors_freeze (vectors);
+      gimp_bezier_stroke_lineto (stroke, &coord0);
+      gimp_vectors_thaw (vectors);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub vectors_bezier_stroke_conicto {
+    $blurb = 'Extends a bezier stroke with a conic bezier spline.';
+
+    $help = <<'HELP';
+Extends a bezier stroke with a conic bezier spline. Actually a
+cubic bezier spline gets added that realizes the shape of a conic
+bezier spline.
+HELP
+
+    &simon_pdb_misc('2005', '2.4');
+
+    @inargs = (
+       { name => 'vectors', type => 'vectors',
+         desc => 'The vectors object' },
+       { name => 'stroke_id', type => 'int32',
+         desc => 'The stroke ID' },
+        { name => 'x0', type => 'float',
+          desc => 'The x-coordinate of the control point' },
+        { name => 'y0', type => 'float',
+          desc => 'The y-coordinate of the control point' },
+        { name => 'x1', type => 'float',
+          desc => 'The x-coordinate of the end point' },
+        { name => 'y1', type => 'float',
+          desc => 'The y-coordinate of the end point' }
+    );
+
+    %invoke = (
+        code => <<"CODE"
+{
+  GimpStroke *stroke = gimp_pdb_get_vectors_stroke (vectors, stroke_id,
+                                                    GIMP_PDB_ITEM_CONTENT, error);
+
+  if (stroke)
+    {
+      GimpCoords coord0 = GIMP_COORDS_DEFAULT_VALUES;
+      GimpCoords coord1 = GIMP_COORDS_DEFAULT_VALUES;
+
+      coord0.x = x0;
+      coord0.y = y0;
+
+      coord1.x = x1;
+      coord1.y = y1;
+
+     if (gimp_item_is_attached (GIMP_ITEM (vectors)))
+       gimp_image_undo_push_vectors_mod (gimp_item_get_image (GIMP_ITEM (vectors)),
+                                         _("Extend path stroke"),
+                                         vectors);
+
+      gimp_vectors_freeze (vectors);
+      gimp_bezier_stroke_conicto (stroke, &coord0, &coord1);
+      gimp_vectors_thaw (vectors);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub vectors_bezier_stroke_cubicto {
+    $blurb = 'Extends a bezier stroke with a cubic bezier spline.';
+
+    $help = <<'HELP';
+Extends a bezier stroke with a cubic bezier spline.
+HELP
+
+    &simon_pdb_misc('2005', '2.4');
+
+    @inargs = (
+       { name => 'vectors', type => 'vectors',
+         desc => 'The vectors object' },
+       { name => 'stroke_id', type => 'int32',
+         desc => 'The stroke ID' },
+        { name => 'x0', type => 'float',
+          desc => 'The x-coordinate of the first control point' },
+        { name => 'y0', type => 'float',
+          desc => 'The y-coordinate of the first control point' },
+        { name => 'x1', type => 'float',
+          desc => 'The x-coordinate of the second control point' },
+        { name => 'y1', type => 'float',
+          desc => 'The y-coordinate of the second control point' },
+        { name => 'x2', type => 'float',
+          desc => 'The x-coordinate of the end point' },
+        { name => 'y2', type => 'float',
+          desc => 'The y-coordinate of the end point' }
+    );
+
+    %invoke = (
+        code => <<"CODE"
+{
+  GimpStroke *stroke = gimp_pdb_get_vectors_stroke (vectors, stroke_id,
+                                                    GIMP_PDB_ITEM_CONTENT, error);
+
+  if (stroke)
+    {
+      GimpCoords coord0 = GIMP_COORDS_DEFAULT_VALUES;
+      GimpCoords coord1 = GIMP_COORDS_DEFAULT_VALUES;
+      GimpCoords coord2 = GIMP_COORDS_DEFAULT_VALUES;
+
+      coord0.x = x0;
+      coord0.y = y0;
+
+      coord1.x = x1;
+      coord1.y = y1;
+
+      coord2.x = x2;
+      coord2.y = y2;
+
+     if (gimp_item_is_attached (GIMP_ITEM (vectors)))
+       gimp_image_undo_push_vectors_mod (gimp_item_get_image (GIMP_ITEM (vectors)),
+                                         _("Extend path stroke"),
+                                         vectors);
+
+      gimp_vectors_freeze (vectors);
+      gimp_bezier_stroke_cubicto (stroke, &coord0, &coord1, &coord2);
+      gimp_vectors_thaw (vectors);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub vectors_bezier_stroke_new_ellipse {
+    $blurb = 'Adds a bezier stroke describing an ellipse the vectors object.';
+
+    $help = <<'HELP';
+Adds a bezier stroke describing an ellipse the vectors object.
+HELP
+
+    &simon_pdb_misc('2005', '2.4');
+
+    @inargs = (
+       { name => 'vectors', type => 'vectors',
+         desc => 'The vectors object' },
+        { name => 'x0', type => 'float',
+          desc => 'The x-coordinate of the center' },
+        { name => 'y0', type => 'float',
+          desc => 'The y-coordinate of the center' },
+        { name => 'radius_x', type => 'float',
+          desc => 'The radius in x direction' },
+        { name => 'radius_y', type => 'float',
+          desc => 'The radius in y direction' },
+        { name => 'angle', type => 'float',
+          desc => 'The angle the x-axis of the ellipse
+                   (radians, counterclockwise)' }
+    );
+
+    @outargs = (
+        { name => 'stroke_id', type => 'int32',
+          desc => 'The resulting stroke' }
+    );
+
+    %invoke = (
+        code => <<"CODE"
+{
+  if (gimp_pdb_item_is_modifyable (GIMP_ITEM (vectors),
+                                   GIMP_PDB_ITEM_CONTENT, error) &&
+      gimp_pdb_item_is_not_group (GIMP_ITEM (vectors), error))
+    {
+      GimpStroke *stroke;
+      GimpCoords  coord0 = GIMP_COORDS_DEFAULT_VALUES;
+
+      coord0.x = x0;
+      coord0.y = y0;
+
+      stroke = gimp_bezier_stroke_new_ellipse (&coord0, radius_x, radius_y, angle);
+
+      if (gimp_item_is_attached (GIMP_ITEM (vectors)))
+        gimp_image_undo_push_vectors_mod (gimp_item_get_image (GIMP_ITEM (vectors)),
+                                          _("Add path stroke"),
+                                          vectors);
+
+      gimp_vectors_stroke_add (vectors, stroke);
+      g_object_unref (stroke);
+
+      stroke_id = gimp_stroke_get_ID (stroke);
+    }
+  else
+    success = FALSE;
+}
+CODE
+    );
+}
+
+sub vectors_to_selection {
+    &std_pdb_deprecated ('gimp-image-select-item');
+    &simon_pdb_misc('2006', '2.4');
+
+    @inargs = (
+        { name => 'vectors', type => 'vectors',
+          desc => 'The vectors object to render to the selection' },
+        { name => 'operation', type => 'enum GimpChannelOps',
+          desc => 'The desired operation with current selection' },
+        { name => 'antialias', type => 'boolean',
+          desc => 'Antialias selection.' },
+        { name => 'feather', type => 'boolean',
+          desc => 'Feather selection.' },
+        { name => 'feather_radius_x', type => 'float',
+          desc => 'Feather radius x.'  },
+        { name => 'feather_radius_y', type => 'float',
+          desc => 'Feather radius y.'  }
+    );
+
+    %invoke = (
+        code => <<'CODE'
+{
+  if (gimp_pdb_item_is_attached (GIMP_ITEM (vectors), NULL, 0, error))
+    gimp_item_to_selection (GIMP_ITEM (vectors),
+                            operation,
+                            antialias,
+                            feather,
+                            feather_radius_x,
+                            feather_radius_y);
+  else
+    success = FALSE;
+}
+CODE
+    );
+
+}
+
+sub vectors_import_from_file {
+    $blurb = 'Import paths from an SVG file.';
+
+    $help = <<'HELP';
+This procedure imports paths from an SVG file. SVG elements other than
+paths and basic shapes are ignored.
+HELP
+
+    &simon_pdb_misc('2006', '2.4');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'filename', type => 'string', allow_non_utf8 => 1,
+          non_empty => 1,
+          desc => 'The name of the SVG file to import.' },
+        { name => 'merge', type => 'boolean',
+          desc => 'Merge paths into a single vectors object.' },
+        { name => 'scale', type => 'boolean',
+          desc => 'Scale the SVG to image dimensions.' }
+    );
+
+    @outargs = (
+        { name => 'vectors_ids', type => 'int32array', void_ret => 1,
+          desc => 'The list of newly created vectors',
+          array => { name => 'num_vectors',
+                     desc => 'The number of newly created vectors' } }
+    );
+
+    %invoke = (
+        headers => [ qw("vectors/gimpvectors-import.h") ],
+        code    => <<'CODE'
+{
+  GFile *file         = g_file_new_for_path (filename);
+  GList *vectors_list = NULL;
+
+  /* FIXME tree */
+  success = gimp_vectors_import_file (image, file,
+                                      merge, scale, NULL, -1,
+                                      &vectors_list, error);
+
+  g_object_unref (file);
+
+  if (success)
+    {
+      num_vectors = g_list_length (vectors_list);
+
+      if (num_vectors)
+        {
+          GList *list;
+          gint   i;
+      
+          vectors_ids = g_new (gint32, num_vectors);
+      
+         list = vectors_list;
+          for (i = 0; i < num_vectors; i++, list = g_list_next (list))
+            vectors_ids[i] = gimp_item_get_ID (GIMP_ITEM (list->data));
+
+          g_list_free (vectors_list);
+        }
+    }
+}
+CODE
+    );
+}
+
+sub vectors_import_from_string {
+    $blurb = 'Import paths from an SVG string.';
+
+    $help = <<'HELP';
+This procedure works like gimp_vectors_import_from_file() but takes a string
+rather than reading the SVG from a file. This allows you to write scripts that
+generate SVG and feed it to GIMP.
+HELP
+
+    &simon_pdb_misc('2006', '2.4');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'string', type => 'string', allow_non_utf8 => 1,
+          desc => 'A string that must be a complete and valid SVG document.' },
+        { name => 'length', type => 'int32',
+          desc => 'Number of bytes in string or -1 if the string is NULL
+                   terminated.' },
+        { name => 'merge', type => 'boolean',
+          desc => 'Merge paths into a single vectors object.' },
+        { name => 'scale', type => 'boolean',
+          desc => 'Scale the SVG to image dimensions.' }
+    );
+
+    @outargs = (
+        { name => 'vectors_ids', type => 'int32array', void_ret => 1,
+          desc => 'The list of newly created vectors',
+          array => { name => 'num_vectors',
+                     desc => 'The number of newly created vectors' } }
+    );
+
+    %invoke = (
+        headers => [ qw("vectors/gimpvectors-import.h") ],
+        code    => <<'CODE'
+{
+  GList *list, *vectors_list = NULL;
+
+  /* FIXME tree */
+  success = gimp_vectors_import_buffer (image, string, length,
+                                        merge, scale, NULL, -1,
+                                        &vectors_list, error);
+
+  if (success)
+    {
+      num_vectors = g_list_length (vectors_list);
+
+      if (num_vectors)
+        {
+          gint i;
+      
+          vectors_ids = g_new (gint32, num_vectors);
+      
+         list = vectors_list;
+          for (i = 0; i < num_vectors; i++, list = g_list_next (list))
+            vectors_ids[i] = gimp_item_get_ID (GIMP_ITEM (list->data));
+
+          g_list_free (vectors_list);
+        }
+    }
+}
+CODE
+    );
+}
+
+sub vectors_export_to_file {
+    $blurb = 'save a path as an SVG file.';
+
+    $help = <<'HELP';
+This procedure creates an SVG file to save a Vectors object, that is,
+a path.  The resulting file can be edited using a vector graphics
+application, or later reloaded into GIMP.  If you pass 0 as the 'vectors'
+argument, then all paths in the image will be exported.
+HELP
+
+    &bill_pdb_misc('2007', '2.6');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'filename', type => 'string', allow_non_utf8 => 1,
+          non_empty => 1,
+          desc => 'The name of the SVG file to create.' },
+        { name => 'vectors', type => 'vectors', no_validate => 1,
+          desc => 'The vectors object to be saved, or 0 for all in the image' }
+    );
+
+    %invoke = (
+        headers => [ qw("vectors/gimpvectors-export.h") ],
+        code    => <<'CODE'
+{
+  GFile *file = g_file_new_for_path (filename);
+
+  success = gimp_vectors_export_file (image, vectors, file, error);
+
+  g_object_unref (file);
+}
+CODE
+    );
+}
+
+sub vectors_export_to_string {
+    $blurb = 'Save a path as an SVG string.';
+
+    $help = <<'HELP';
+This procedure works like gimp_vectors_export_to_file() but creates a string
+rather than a file. The contents are a NUL-terminated string that holds a 
+complete XML document.  If you pass 0 as the 'vectors' argument, then all
+paths in the image will be exported.
+HELP
+
+    &bill_pdb_misc('2007', '2.6');
+
+    @inargs = (
+        { name => 'image', type => 'image',
+          desc => 'The image' },
+        { name => 'vectors', type => 'vectors', no_validate => 1,
+          desc => 'The vectors object to save, or 0 for all in the image' }
+    );
+
+    @outargs = (
+        { name => 'string', type => 'string',
+          desc => 'A string whose contents are a complete SVG document.' }
+    );
+
+    %invoke = (
+        headers => [ qw("vectors/gimpvectors-export.h") ],
+        code    => <<'CODE'
+{
+  string = gimp_vectors_export_string (image, vectors);
+
+  success = (string != NULL);
+}
+CODE
+    );
+}
+
+
+@headers = qw(<string.h>
+              "core/gimplist.h"
+              "core/gimpimage.h"
+              "core/gimpimage-undo-push.h"
+             "text/gimptext-vectors.h"
+             "text/gimptextlayer.h"
+              "vectors/gimpanchor.h"
+              "vectors/gimpstroke-new.h"
+              "vectors/gimpbezierstroke.h"
+              "vectors/gimpvectors.h"
+              "gimppdb-utils.h"
+              "gimp-intl.h");
+
+@procs = qw(vectors_new
+           vectors_new_from_text_layer
+            vectors_copy
+            vectors_get_strokes 
+            vectors_stroke_get_length
+            vectors_stroke_get_point_at_dist
+            vectors_remove_stroke
+            vectors_stroke_close
+            vectors_stroke_translate
+            vectors_stroke_scale
+            vectors_stroke_rotate
+            vectors_stroke_flip
+            vectors_stroke_flip_free
+            vectors_stroke_get_points
+            vectors_stroke_new_from_points
+            vectors_stroke_interpolate
+            vectors_bezier_stroke_new_moveto
+            vectors_bezier_stroke_lineto
+            vectors_bezier_stroke_conicto
+            vectors_bezier_stroke_cubicto
+            vectors_bezier_stroke_new_ellipse
+            vectors_to_selection
+            vectors_import_from_file
+            vectors_import_from_string
+            vectors_export_to_file
+            vectors_export_to_string);
+
+%exports = (app => [@procs], lib => [@procs]);
+
+$desc = 'Vectors';
+$doc_title = 'gimpvectors';
+$doc_short_desc = 'Functions for querying and manipulating vectors.';
+$doc_long_desc = 'Functions for querying and manipulating vectors.';
+
+1;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]