Beispiel #1
0
def prepare_makeplayingcards(img, drawable, output_folder):
    """ Prepare an image for MakePlayingCards printing.
    """
    gimp.progress_init('Prepare an image for MakePlayingCards printing...')
    pdb.gimp_undo_push_group_start(img)

    try:
        file_name, back_side = _get_filename_backside(img)
    except Exception:  # pylint: disable=W0703
        pdb.gimp_undo_push_group_end(img)
        return

    rotation = _get_rotation(drawable)
    clip_size = _get_mpc_clip_size(drawable)

    if rotation:
        _rotate(drawable, back_side)

    if clip_size:
        _clip(img, drawable, clip_size, rotation and back_side)

    pdb.file_png_save(img, drawable,
                      os.path.join(output_folder, file_name), file_name,
                      0, 9, 1, 0, 0, 1, 1)
    pdb.gimp_undo_push_group_end(img)
Beispiel #2
0
def prepare_pdf_back(img, drawable, output_folder):
    """ Prepare a back image for PDF document.
    """
    gimp.progress_init('Prepare a back image for PDF document...')
    pdb.gimp_undo_push_group_start(img)

    try:
        file_name, back_side = _get_filename_backside(img)
    except Exception:  # pylint: disable=W0703
        pdb.gimp_undo_push_group_end(img)
        return

    if not back_side:
        pdb.gimp_undo_push_group_end(img)
        return

    rotation = _get_rotation(drawable)

    if rotation:
        _rotate(drawable, back_side)

    pdb.file_png_save(img, drawable,
                      os.path.join(output_folder, file_name), file_name,
                      0, 9, 1, 0, 0, 1, 1)
    pdb.gimp_undo_push_group_end(img)
def file_load_lepton(filename, raw_filename):
    ''' Save the current layer into a PNG file, a JPEG file and a BMP file.

    Parameters:
    image : image The current image.
    layer : layer The layer of the image that is selected.
    file : string The file to open in a new layer.
    '''
    # Indicates that the process has started.
    gimp.progress_init("Opening '" + filename + "'...")

    try:
        tmpdirobj = mkdtemp()
        tmp_dirname = tmpdirobj
        jpeg_fn = os.path.join(tmp_dirname, "from_lep.jpeg")
        subprocess.check_call(["lepton", filename, jpeg_fn])
        fileImage = pdb.file_jpeg_load(jpeg_fn, filename)
        if (fileImage is None):
            gimp.message("The image could not be opened since" +
                         "it is not an image file.")
        shutil.rmtree(tmp_dirname)
        return fileImage
    except Exception as err:
        gimp.message("Unexpected error: " + str(err))
        raise err
def prepare_tts(img, _, output_folder):  # pylint: disable=R0914
    """ Prepare a TTS sheet image.
    """
    gimp.progress_init('Prepare a TTS sheet image...')
    pdb.gimp_undo_push_group_start(img)

    try:
        file_name, _ = _get_filename_backside(img, 'jpg')
    except Exception:  # pylint: disable=W0703
        pdb.gimp_undo_push_group_end(img)
        return

    parts = file_name.split('_')
    num = int(parts[-3])
    rows = int(parts[-4])
    columns = int(parts[-5])
    new_width = columns * 750
    new_height = rows * 1050
    pdb.gimp_image_scale(img, new_width, new_height)

    json_path = re.sub(r'\.jpg$', '.json', pdb.gimp_image_get_filename(img))
    try:
        with open(json_path, 'r') as fobj:
            cards = json.load(fobj)
    except Exception:  # pylint: disable=W0703
        pdb.gimp_undo_push_group_end(img)
        return

    cards = [c['path'] for c in cards]
    if len(cards) != num:
        pdb.gimp_undo_push_group_end(img)
        return

    card_rows = [
        cards[i * columns:(i + 1) * columns]
        for i in range((len(cards) + columns - 1) // columns)
    ]
    if len(card_rows) != rows:
        pdb.gimp_undo_push_group_end(img)
        return

    for i, card_row in enumerate(card_rows):
        for j, card_path in enumerate(card_row):
            if not os.path.exists(card_path):
                pdb.gimp_undo_push_group_end(img)
                return

            card_layer = pdb.gimp_file_load_layer(img, card_path)
            pdb.gimp_image_insert_layer(img, card_layer, None, -1)
            rotation = _get_rotation(card_layer)
            if rotation:
                _rotate(card_layer, True)

            pdb.gimp_layer_set_offsets(card_layer, j * 750, i * 1050)
            pdb.gimp_image_merge_down(img, card_layer, 1)

    pdb.file_jpeg_save(img, img.layers[0],
                       os.path.join(output_folder, file_name), file_name, 1, 0,
                       1, 0, '', 2, 1, 0, 0)
    pdb.gimp_undo_push_group_end(img)
def prepare_pdf_front_old(img, drawable, output_folder):
    """ Prepare a front image for PDF document. [OLD VERSION]
    """
    gimp.progress_init('Prepare a front image for PDF document...')
    pdb.gimp_undo_push_group_start(img)

    try:
        file_name, back_side = _get_filename_backside(img)
    except Exception:  # pylint: disable=W0703
        pdb.gimp_undo_push_group_end(img)
        return

    if back_side:
        pdb.gimp_undo_push_group_end(img)
        return

    rotation = _get_rotation(drawable)
    margin_size = _get_pdf_margin_size(drawable)
    if rotation:
        _rotate(drawable, back_side)

    if margin_size:
        _add_margin(img, drawable, margin_size)

    clip_size = _get_pdf_clip_size(drawable)
    if clip_size:
        _clip(img, drawable, clip_size, rotation and back_side)

    pdb.file_png_save(img, drawable, os.path.join(output_folder, file_name),
                      file_name, 0, 9, 1, 0, 0, 1, 1)
    pdb.gimp_undo_push_group_end(img)
Beispiel #6
0
def prepare_drivethrucards(img, drawable, output_folder):
    """ Prepare an image for DriveThruCards printing.
    """
    gimp.progress_init('Prepare an image for DriveThruCards printing...')
    pdb.gimp_undo_push_group_start(img)

    try:
        file_name, back_side = _get_filename_backside(img, 'jpg')
    except Exception:  # pylint: disable=W0703
        pdb.gimp_undo_push_group_end(img)
        return

    rotation = _get_rotation(drawable)
    clip_size = _get_dtc_clip_size(drawable)

    if rotation:
        _rotate(drawable, back_side)

    if clip_size:
        _clip(img, drawable, clip_size, rotation and back_side)

    pdb.file_jpeg_save(img, drawable,
                       os.path.join(output_folder, file_name), file_name,
                       1, 0, 1, 0, '', 2, 1, 0, 0)
    pdb.gimp_undo_push_group_end(img)
    def __copyAndPasteForegroundImageAsNew(self):
        """
		Paste the foreground image onto a new background image.

		Instruct GIMP to copy the forground image into its buffer, then paste the contents of this buffer into a new background image.


		Parameters:

		NA


		Returns:

		NA


		Invoked by :

		__run


		Invokes :

		__copyForegroundImageIntoBuffer

		"""

        nameProcedure = "OverlayImageAgent::__copyAndPasteForegroundImageAsNew"

        print("%s : Enter" % (nameProcedure))

        # Copy the Foreground Image into the Buffer.

        self.__copyForegroundImageIntoBuffer()

        # Paste the Foreground Image from the Buffer onto the Background Image.

        self.image_background_new = pdb.gimp_edit_paste_as_new(
            self.drawable_background, True)

        print("%s : Type = %s" %
              (nameProcedure, type(self.image_background_new)))

        if (self.image_background_new == -1):

            print(
                "%s : Attempted to paste from the Edit buffer, but it appears to be empty."
                % (nameProcedure))

            exception_message = "\n\nAn Exception has been raised by the method;\n\n  " + \
                                nameProcedure + \
                                "\n\nThis method attempted to paste from the Edit buffer, but it appears to be empty.\n\nAs a result, this Plugin is about to terminate!"

            raise Exception(exception_message)

        gimp.progress_init(
            "Have pasted foreground image from Buffer onto background image")

        print("%s : Exit" % (nameProcedure))
def png(image=None):
    if not image:
        image = gimp.image_list()[0]
    prefix = pdb.gimp_image_get_filename(image)[:-4] + "_"
    gimp.progress_init("Save frames as {}_*.png".format(prefix))
    for (layer_index, layer) in enumerate(image.layers):
        try:
            filename = "{}{:02d}.png".format(prefix, int(layer.name))
        except ValueError:
            filename = "{}{}.png".format(prefix, layer.name)
        pdb.file_png_save(
            image,
            layer,
            filename,
            None,
            True,  # interlace
            9,  # compression
            True,  # bkgd
            True,  # gama
            True,  # offs
            True,  # phys
            True,  # time
        )
        gimp.progress_update(100 * (layer_index + 1) / len(image.layers))
    gimp.message("All frames saved as {}_*.png".format(prefix))
Beispiel #9
0
 def run_outer(self, gimp_img, drawable, *extra_args):
     self.gimp_img = gimp_img
     self.drawable = drawable
     print("Running {}...".format(self.name))
     pdb.gimp_image_undo_group_start(self.gimp_img)
     gimp.progress_init("Running {}...".format(self.name))
     self.run(*extra_args)
     pdb.gimp_image_undo_group_end(self.gimp_img)
Beispiel #10
0
def halftone_layer(img, layer, layer_dest, density, color, circle_size,
                   black_strength):
    """ halftone the layer """
    gimp.progress_init("Generating " + layer_dest.name)
    circles_count = 0
    start = timer()

    source_region = layer.get_pixel_rgn(0, 0, layer.width, layer.height, False,
                                        False)
    pixel_size = len(source_region[0, 0])
    source_pixels = array("B", source_region[0:layer.width, 0:layer.height])
    print(pixel_size, len(source_pixels))

    region = layer_dest.get_pixel_rgn(0, 0, layer_dest.width,
                                      layer_dest.height, True, True)
    # pixel_size = 4
    n = layer_dest.width * layer_dest.height
    pixels = array("B", "\xFF" * (n * pixel_size))

    for i in range(0, (layer.height / density) + 1):
        for j in range(0, (layer.width / density) + 1):
            percent = ((i * layer.width + j) / float(
                (layer.height * layer.width))) * density
            gimp.progress_update(percent)
            x = j * density
            y = i * density
            x_2 = x + density if x + density < layer.width else layer.width
            y_2 = y + density if y + density < layer.height else layer.height
            if x_2 == x or y_2 == y:
                continue
            box = (x, y, x_2, y_2)
            mean_bright = get_mean_brightness(source_pixels, box, layer.width,
                                              layer.height, pixel_size)
            max_strength = 255
            if color == (0, 0, 0, 255):
                # manage black strength
                max_strength = 510 - black_strength

            isz = get_value_in_range(mean_bright, 0, max_strength, 0,
                                     circle_size)

            if isz > 0:
                circles_count += 1
                draw_circle_on_pixels_region(pixels, layer_dest.width,
                                             layer_dest.height,
                                             x + density / 2, y + density / 2,
                                             isz, color)

    # Write our changes back over the original layer
    region[0:layer_dest.width, 0:layer_dest.height] = pixels.tostring()
    layer_dest.flush()
    layer_dest.merge_shadow(True)
    layer_dest.update(0, 0, layer_dest.width, layer_dest.height)

    pdb.gimp_progress_end()
    end = timer()
    print(circles_count, 'in', end - start, float(end - start) / circles_count)
Beispiel #11
0
def MDFade(image, layer, srcmode, dstmode, fademode):
    srclut = SelectSrcLUT(srcmode)
    dstlut = SelectDstLUT(dstmode)
    gimp.progress_init("Generating palette fade...")
    pdb.gimp_image_undo_group_start(image)
    # Get the layer position.
    pos = FindLayer(image, layer)
    srcWhite = FindColor(255, srcmode)
    if (fademode == FadeMode.CurrentToBlack):
        conv = lambda jj, ss: (jj * (15 - ss)) // 15
    elif (fademode == FadeMode.BlackToCurrent):
        conv = lambda jj, ss: (jj * ss) // 15
    elif (fademode == FadeMode.CurrentToWhite):
        conv = lambda jj, ss: (jj * (15 - ss) + srcWhite * ss) // 15
    else:  #if (fademode == FadeMode.WhiteToCurrent):
        conv = lambda jj, ss: (jj * ss + srcWhite * (15 - ss)) // 15
    finalLayer = None
    for step in xrange(15):
        lut = {
            chr(ii): chr(dstlut[srclut[conv(ii, step)]])
            for ii in xrange(256)
        }
        # Create a new layer to save the results (otherwise is not possible to undo the operation).
        newLayer = layer.copy()
        image.add_layer(newLayer, pos)
        # Clear the new layer.
        pdb.gimp_edit_clear(newLayer)
        newLayer.flush()
        # Calculate the number of tiles.
        tile_width = gimp.tile_width()
        tile_height = gimp.tile_height()
        # Ceiling division
        width_tiles = -(-layer.width // tile_width)
        height_tiles = -(-layer.height // tile_height)
        # Iterate over the tiles.
        for col in xrange(width_tiles):
            for row in xrange(height_tiles):
                # Update the progress bar.
                gimp.progress_update(
                    float(step * width_tiles * height_tiles +
                          col * height_tiles + row) /
                    float(15 * width_tiles * height_tiles))
                ConvertTileNoSHL(layer.get_tile(False, row, col),
                                 newLayer.get_tile(False, row, col), lut)
        # Update the new layer.
        newLayer.flush()
        newLayer.merge_shadow(True)
        newLayer.update(0, 0, layer.width, layer.height)
        finalLayer = newLayer
    # Remove the old layer.
    if finalLayer is not None:
        layerName = layer.name
        image.remove_layer(layer)
        finalLayer.name = layerName
    gimp.displays_flush()
    pdb.gimp_image_undo_group_end(image)
    def __getForegroundImageAndDrawable(self, filename_foreground):
        """
		Use GIMP to get the image and drawable objects from the forground image.


		Parameters:

		filename_forground (String) : The filename of the forground image file.


		Returns:

		NA


		Invoked by :

		OverlayImageAgent.runMultipleFromList


		Invokes :

		NA
		"""

        nameProcedure = "OverlayImageAgent::__getImageAndDrawable"

        print("%s : Enter" % (nameProcedure))

        # Get the following from both the foreground image file;
        #
        #   - image
        #   - drawable
        #
        # and save them into the appropriate global variables.

        self.image_foreground = pdb.gimp_file_load(filename_foreground,
                                                   filename_foreground)
        self.drawable_foreground = pdb.gimp_image_get_active_layer(
            self.image_foreground)

        print("Filename foreground image = %s" %
              self.image_foreground.filename)

        gimp.progress_init(
            "Have got image and drawable from Foreground Image file")

        print("%s : Exit" % (nameProcedure))
def prepare_db_output(img, drawable, output_folder):
    """ Prepare an image for DB output.
    """
    gimp.progress_init('Prepare an image for DB output...')
    pdb.gimp_undo_push_group_start(img)

    try:
        file_name, _ = _get_filename_backside(img)
    except Exception:  # pylint: disable=W0703
        pdb.gimp_undo_push_group_end(img)
        return

    pdb.script_fu_round_corners(img, drawable, 40, 0, 0, 0, 0, 0, 0)

    pdb.file_png_save(img, drawable, os.path.join(output_folder, file_name),
                      file_name, 0, 9, 1, 0, 0, 1, 1)
    pdb.gimp_undo_push_group_end(img)
def prepare_mbprint_jpg(img, drawable, output_folder):
    """ Prepare a JPG image for MBPrint printing.
    """
    gimp.progress_init('Prepare a JPG image for MBPrint printing...')
    pdb.gimp_undo_push_group_start(img)

    try:
        file_name, back_side = _get_filename_backside(img, 'jpg')
    except Exception:  # pylint: disable=W0703
        pdb.gimp_undo_push_group_end(img)
        return

    rotation = _get_rotation(drawable)
    if rotation:
        _rotate(drawable, back_side)

    pdb.file_jpeg_save(img, drawable, os.path.join(output_folder, file_name),
                       file_name, 1, 0, 1, 0, '', 2, 1, 0, 0)
    pdb.gimp_undo_push_group_end(img)
def cut_bleed_margins(img, drawable, output_folder):
    """ Cut bleed margins from an image.
    """
    gimp.progress_init('Cut bleed margins from an image...')
    pdb.gimp_undo_push_group_start(img)

    try:
        file_name, _ = _get_filename_backside(img)
    except Exception:  # pylint: disable=W0703
        pdb.gimp_undo_push_group_end(img)
        return

    clip_size = _get_bleed_margin_size(drawable)
    if clip_size:
        _clip(img, drawable, clip_size, False)

    pdb.file_png_save(img, drawable, os.path.join(output_folder, file_name),
                      file_name, 0, 9, 1, 0, 0, 1, 1)
    pdb.gimp_undo_push_group_end(img)
    def __copyAndPasteForegroundImageIntoNewLayer(self):
        """
		Paste the foreground image onto a new layer of the background image.

		Instruct GIMP to copy the forground image into its buffer, then paste the contents of this buffer into a new layer of the background image.


		Parameters:

		NA


		Returns:

		NA


		Invoked by :

		__run


		Invokes :

		__copyForegroundImageIntoBuffer

		"""

        nameProcedure = "OverlayImageAgent::__copyAndPasteForegroundImageIntoNewLayer"

        print("%s : Enter" % (nameProcedure))

        # Copy the Foreground Image into the Buffer.

        self.__copyForegroundImageIntoBuffer()

        # Paste the Foreground Image from the Buffer onto the Background Image.

        # WORK OUT WHAT TO DO HERE!!!

        gimp.progress_init("Have pasted FG Image from Buffer into new Layer")

        print("%s : Exit" % (nameProcedure))
Beispiel #17
0
def MDColors(image, layer, srcmode, dstmode, shlmode):
    lut = BuildColorLUT(srcmode, dstmode, shlmode)
    gimp.progress_init("Converting to MD colors...")
    # Indexed images are faster
    if layer.is_indexed:
        ConvertColormap(image, lut)
    else:
        pdb.gimp_image_undo_group_start(image)
        # Get the layer position.
        pos = FindLayer(image, layer)
        # Create a new layer to save the results (otherwise is not possible to undo the operation).
        newLayer = layer.copy()
        image.add_layer(newLayer, pos)
        layerName = layer.name
        # Clear the new layer.
        pdb.gimp_edit_clear(newLayer)
        newLayer.flush()
        # Calculate the number of tiles.
        tile_width = gimp.tile_width()
        tile_height = gimp.tile_height()
        # Ceiling division
        width_tiles = -(-layer.width // tile_width)
        height_tiles = -(-layer.height // tile_height)
        # Iterate over the tiles.
        for col in xrange(width_tiles):
            for row in xrange(height_tiles):
                # Update the progress bar.
                gimp.progress_update(
                    float(col * height_tiles + row) /
                    float(width_tiles * height_tiles))
                ConvertTile(layer.get_tile(False, row, col),
                            newLayer.get_tile(False, row, col), lut)
        # Update the new layer.
        newLayer.flush()
        newLayer.merge_shadow(True)
        newLayer.update(0, 0, layer.width, layer.height)
        # Remove the old layer.
        image.remove_layer(layer)
        newLayer.name = layerName
        # Update display and finish undo group.
        gimp.displays_flush()
        pdb.gimp_image_undo_group_end(image)
    def __copyForegroundImageIntoBuffer(self):
        """
		Instruct GIMP to copy the foreground image into its buffer.


		Parameters:

		NA


		Returns:

		NA


		Invoked by :

		OverlayImageAgent.__copyAndPasteForegroundImage
		OverlayImageAgent.__copyAndPasteForegroundImageAsNew
		OverlayImageAgent.__copyAndPasteForegroundImageIntoNewLayer


		Invokes :

		OverlayImageAgent.__pauseAndProgressDisplay
		"""

        nameProcedure = "OverlayImageAgent::__copyForegroundImageIntoBuffer"

        print("%s : Enter" % (nameProcedure))

        # Copy the Foreground Image into the Buffer.

        copy_result = pdb.gimp_edit_copy(self.drawable_foreground)

        print("%s : Copy result = %s" % (nameProcedure, str(copy_result)))

        # Update the Progress Bar in the Plugin Window.

        gimp.progress_init("Have copied foreground image into Buffer")

        self.__pauseAndProgressDisplay(self.sleep_timer)
def gif(image=None, suffix=None, fps=24):
    if not image:
        image = gimp.image_list()[0]
    file_path = pdb.gimp_image_get_filename(image)[:-4]
    if suffix:
        file_path += "_" + suffix.strip()
    file_path += ".gif"
    ms = int(1000.0 / fps)
    temp_image = False
    try:
        gimp.progress_init(
            "Save animated GIF @ {} fps = {} ms/frame as {}".format(
                fps, ms, file_path))
        if pdb.gimp_image_base_type(image) != 2:
            temp_image = True
            image = pdb.gimp_image_duplicate(image)
            pdb.gimp_image_convert_indexed(
                image,
                0,  # dither-type=CONVERT-DITHER-NONE
                0,  # palette-type=CONVERT-PALETTE-GENERATE
                255,  # num-cols
                False,  # alpha-dither
                False,  # remove-unused
                ""  # palette
            )
            gimp.progress_update(50)
        pdb.file_gif_save(
            image,
            image.layers[0],
            file_path,
            file_path,
            True,  # interlace
            True,  # loop
            ms,  # default-delay
            2  # default-dispose
        )
        gimp.progress_update(100)
        gimp.message("Saved animated GIF @ {} fps = {} ms/frame as {}".format(
            fps, ms, file_path))
    finally:
        if temp_image:
            pdb.gimp_image_delete(image)
def \
scale_and_set_size_interactive(

        image,
        drawable,
        horizontalResolution,
        verticalResolution,
        scalingFactorPreference,
        horizontalOffset,
        verticalOffset,
        interpolationMode,
        filename
) :

    nameFunction = "scale_and_set_size_noninteractive"

    print("%s : Enter" % (nameFunction))

    gimp.progress_init("Scaling and setting the size of image : " +
                       image.filename)

    # Start a GIMP Undo group, as this will allow the actions of this Plugin to be undone in one step.

    pdb.gimp_undo_push_group_start(image)

    scaleAndSetSizeObject = ScaleAndSetSizeObject(
        image, drawable, horizontalResolution, verticalResolution,
        scalingFactorPreference, horizontalOffset, verticalOffset,
        interpolationMode, filename)

    scaleAndSetSizeObject.run()

    # End the GIMP Undo group.

    pdb.gimp_undo_push_group_end(image)

    print("%s : Exit" % (nameFunction))
def prepare_db_output_folder(input_folder, output_folder):
    """ Prepare a folder of images for DB output.
    """
    gimp.progress_init('Prepare a folder of images for DB output...')
    _iterate_folder(input_folder, output_folder, prepare_db_output)
Beispiel #22
0
 def progress_init(self, text):
     from gimpfu import gimp
     gimp.progress_init(text)
Beispiel #23
0
def prepare_drivethrucards_folder(input_folder, output_folder):
    """ Prepare a folder of images for DriveThruCards printing.
    """
    gimp.progress_init(
        'Prepare a folder of images for DriveThruCards printing...')
    _iterate_folder(input_folder, output_folder, prepare_drivethrucards)
Beispiel #24
0
def prepare_makeplayingcards_folder(input_folder, output_folder):
    """ Prepare a folder of images for MakePlayingCards printing.
    """
    gimp.progress_init(
        'Prepare a folder of images for MakePlayingCards printing...')
    _iterate_folder(input_folder, output_folder, prepare_makeplayingcards)
Beispiel #25
0
def prepare_pdf_back_folder(input_folder, output_folder):
    """ Prepare a folder of back images for PDF document.
    """
    gimp.progress_init(
        'Prepare a folder of back images for PDF document...')
    _iterate_folder(input_folder, output_folder, prepare_pdf_back)
def cut_bleed_margins_folder(input_folder, output_folder):
    """ Cut bleed margins from a folder of images.
    """
    gimp.progress_init('Cut bleed margins from a folder of images...')
    _iterate_folder(input_folder, output_folder, cut_bleed_margins)
def prepare_tts_folder(input_folder, output_folder):
    """ Prepare a folder of TTS sheet images.
    """
    gimp.progress_init('Prepare a folder of TTS sheet images...')
    _iterate_folder(input_folder, output_folder, prepare_tts)
def prepare_generic_png_folder(input_folder, output_folder):
    """ Prepare a folder of generic PNG images.
    """
    gimp.progress_init('Prepare a folder of generic PNG images...')
    _iterate_folder(input_folder, output_folder, prepare_generic_png)
def sheet(image=None, cols=0):
    if not image:
        image = gimp.image_list()[0]
    if not cols:
        best = (1, 10000000)
        for cols in range(1, len(image.layers) + 1):
            rows = (len(image.layers) + (cols - 1)) // cols
            (sheet_width, sheet_height) = (cols * image.width,
                                           rows * image.height)
            sheet_aspect_ratio = sheet_width / sheet_height if sheet_width > sheet_height else sheet_height / sheet_width
            if sheet_aspect_ratio < best[1]:
                best = (cols, sheet_aspect_ratio)
        cols = best[0]
    file_path = "{}_sheet_{}_frames_{}_columns_{}x{}.png".format(
        pdb.gimp_image_get_filename(image)[:-4], len(image.layers), cols,
        image.width, image.height)
    gimp.progress_init("Save sheet as {}".format(file_path))
    rows = (len(image.layers) + (cols - 1)) // cols
    sheet = pdb.gimp_image_new(image.width * cols, image.height * rows, 0)
    try:
        sheet_layer = pdb.gimp_layer_new(
            sheet,
            sheet.width,
            sheet.height,
            1,  # type = RGBA-IMAGE
            "sprite sheet",
            100,  # opacity = 100 %
            0  # mode = LAYER-MODE-NORMAL-LEGACY
        )
        pdb.gimp_image_insert_layer(sheet, sheet_layer, None, 0)

        (row, col) = (0, 0)
        for (layer_index, layer) in enumerate(image.layers):
            pdb.gimp_selection_none(image)
            pdb.gimp_layer_resize_to_image_size(layer)
            pdb.gimp_edit_copy(layer)
            floating = pdb.gimp_edit_paste(sheet_layer, True)
            (left, top) = floating.offsets
            pdb.gimp_layer_translate(floating, col * image.width - left,
                                     row * image.height - top)
            pdb.gimp_floating_sel_anchor(floating)
            col += 1
            if col >= cols:
                col = 0
                row += 1
            gimp.progress_update(100 * (layer_index + 1) / len(image.layers))
        pdb.file_png_save(
            sheet,
            sheet_layer,
            file_path,
            None,
            True,  # interlace
            9,  # compression
            True,  # bkgd
            True,  # gama
            True,  # offs
            True,  # phys
            True,  # time
        )
        gimp.message("All frames saved as {}".format(file_path))
    finally:
        pdb.gimp_image_delete(sheet)
def prepare_mbprint_jpg_folder(input_folder, output_folder):
    """ Prepare a folder of images for MBPrint printing (JPG).
    """
    gimp.progress_init(
        'Prepare a folder of images for MBPrint printing (JPG)...')
    _iterate_folder(input_folder, output_folder, prepare_mbprint_jpg)