#!/usr/bin/env python
# -*- coding: iso8859-1 -*-
# $Id: multiborders.py 3238 2007-08-14 20:07:43Z svn $
# version 1.1  - ok with gimp 2.3.19
# Copyright (C) 2007 Jerome Dumonteil <jd@hamete.org>
#
# 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/>.

"""
Multi border plugins generator for The Gimp, to manage resizing to fixed 
format and adding of a monocolor border to photos.

This generate several plugins, each of them in charge of scaling an image
to a definite size (in pixels) with a border (in mm).
Usefull to prepare photos for paper printing.

The plugin are not interactive, they appear as a list of choices in 
the Image/resize and border/ sub menu of Gimp.

The values can be modified as needed in the 'table' below.

The x and y numbers correspond to the size of the file the guy 
printing your photos is expecting, for me it's :
    
4"x6"    = 10x15 cm =  1200 x 1800 pix  at 300 dpi
5"x7.5"  = 13x19 cm  = 1500 x 2250 pix  at 300 dpi
6"x8.27" = 15x21 cm  = 1800 x 2480 pix  at 300 dpi
7"x10.5" = 18x27 cm  = 2100 x 3150 pix  at 300 dpi
8"x12"   = 20x30 cm  = 2400 x 3600 pix  at 300 dpi
12"x18"  = 30x45 cm  = 5316 x 3600 pix  at 300 dpi

"""

from gimpfu import *

# order of the columns in the table below :
# x, y, border, color, dpi, orientation, autosave, comment
#
# x, y       : target paper size in pixel 
# border     : size of the border around the image, in mm
# color      : color of the border (RGB), do not add spaces between numbers!
# dpi        : dot-per-inch, generally 300, to convert border size in pixels
# orientation: default is to use same orientation than the image for the
#              resizing (value: "-"), put a "H" or "V" to force orientation
#              to landscape or portrait.
# autosave   : change the "S" to "-" to not have the new image automatically 
#              saved as a jpeg.
# comment   :  you can add a small comment for the name of the plugin in the
#              menu (no space here! use "_" for space)
#
# If you use more than 20 lines in this table, add some more lines 
# "def scale_border_21, 22, etc... at the end of the file.
#

table="""


1800    1200    4       (255,255,255)   300     -   S   -
1800    1200    6       (255,255,255)   300     -   S   -
1800    1200    6       (255,255,255)   300     V   S   -
1800    1200    6       (192,192,192)   300     -   -   gray
2250    1500    5       (255,255,255)   300     -   S   -
2250    1500    8       (255,255,255)   300     -   S   -
2480    1800    8       (255,255,255)   300     -   S   -
3150    2100    7       (255,255,255)   300     -   S   -
3150    2100    10      (255,255,255)   300     -   S   -
3600    2400    8       (255,255,255)   300     -   S   -
3600    2400    12      (255,255,255)   300     -   S   -
5316    3600    20      (255,255,255)   300     -   S   -
5316    3600    40      (255,255,255)   300     -   S   -
2480    1800    12      (255,255,255)   300     -   S   -
1800    1200    0       (255,255,255)   300     -   -   no_border



"""
   
class MakeBorder:
    """class in charge of computation of the several sizes of image and 
        borders, class is independant from gimpfu"""
    inch=25.4
    def __init__(self,sizex=1800,sizey=1200,border=10,dpi=300):
        # sizex,sizey : sides length of target image in pixels
        # border : side border minimal size in mm (1""=25.4mm)
        # dpi : 300 dot per inch by default
        self.dpi=dpi
        if sizex>sizey:
            self.full_x,self.full_y=sizex,sizey
        else:
            self.full_x,self.full_y=sizey,sizex
        self.border=border
        # minimal size of border in pixels
        self.min_border=int(1.0*self.border/MakeBorder.inch*self.dpi+1)
        self.inner_x=self.full_x-2*self.min_border
        self.inner_y=self.full_y-2*self.min_border
        
    def make_suffix_name(self,ext):
        "suffix name for the new created image autosaved"
        return "%sx%sx%s.%s"%(self.full_x,self.full_y,self.min_border,ext)
    
    def set_orientation(self,horiz=True):
        self.horiz=horiz
    
    def set_image_size(self,h,w):
        self.orig_h=h
        self.orig_w=w
        if self.orig_w>=self.orig_h:
            self.set_orientation(True)
        else:
            self.set_orientation(False)
        
    def define_scale_size(self):
        if self.horiz:
            fw,fh=self.full_x,self.full_y
            mw,mh=self.inner_x,self.inner_y
        else:
            fh,fw=self.full_x,self.full_y
            mh,mw=self.inner_x,self.inner_y
        self.full_h=fh
        self.full_w=fw
        w=mw
        h=int(self.orig_h*1.0*w/self.orig_w)
        if h>mh:
            h=mh
            w=int(self.orig_w*1.0*h/self.orig_h)
        self.scale_h=h
        self.scale_w=w    
        self.offset_h=int((self.full_h-self.scale_h)/2)
        self.offset_w=int((self.full_w-self.scale_w)/2)


def make_border(image,sx,sy,border,dpi,bgcolor=(255,255,255),force="",autosave=True):
    "actual resize and border generation function, using gimpfu"
    # file name, quick hack to remove file extension
    orig_fn=pdb.gimp_image_get_filename(image)
    prefix_fn=orig_fn
    try:
        while prefix_fn[-1]!=".":
            prefix_fn=prefix_fn[:-1]
        prefix_fn=prefix_fn[:-1]
    except:
        pass
    # work on a copy of the original image
    img=pdb.gimp_image_duplicate(image)
    pdb.gimp_display_new(img)
    pdb.gimp_image_undo_freeze(img)
    # initialise border object
    P=MakeBorder(sx,sy,border,dpi)
    new_fn="%s-%s"%(prefix_fn,P.make_suffix_name("jpg"))
    pdb.gimp_image_set_filename(img,new_fn)    
    h=pdb.gimp_image_height(img)
    w=pdb.gimp_image_width(img)
    P.set_image_size(h,w)
    # force orientation (default is to use the maximum space)
    if force.lower()=="v":
        P.set_orientation(False)
    if force.lower()=="h":
        P.set_orientation(True)
    P.define_scale_size()
    pdb.gimp_image_scale(img,P.scale_w,P.scale_h)
    pdb.gimp_image_resize(img,P.full_w,P.full_h,P.offset_w,P.offset_h)
    background=pdb.gimp_layer_new(img,P.full_w,P.full_h,1,"background",100,0)
    current_bg=pdb.gimp_context_get_background()
    pdb.gimp_context_set_background(bgcolor)
    pdb.gimp_drawable_fill(background,1)
    pdb.gimp_context_set_background(current_bg)
    pdb.gimp_image_add_layer(img,background,1)
    pdb.gimp_image_merge_visible_layers(img,0)
    final=pdb.gimp_image_flatten(img)
    pdb.gimp_image_undo_thaw(img)
    pdb.gimp_displays_flush()
    if not autosave:
        return
    # save new image in high quality jpeg format
    pdb.file_jpeg_save(img,img.active_layer,new_fn,new_fn,1.0,0.0,0,0,"",2,0,0,1)
    pdb.gimp_image_clean_all(img)
    

def parse_line(line):
    x,y,border,bgcol,dpi,force,autosave,comment=tuple(line.split()) 
    x=int(x)
    y=int(y)
    border=int(border)
    bgcol=eval(bgcol)
    dpi=int(dpi)
    if comment=="-":
        comment=""
    if autosave=="-":
        autosave=False
    else:
        autosave=True
    return (x,y,border,bgcol,dpi,force,autosave,comment)
    
    
def intialisation():
    "lauched once to register all usable functions and store them in gimp menu"
    author="Jerome Dumonteil"
    copy="GPL v3"
    date="2007"
    params=filter(None,table.splitlines())
    params.sort()
    ref=0
    for line in params:
        t=parse_line(line)
        ref+=1
        x,y,border,bgcol,dpi,force,autosave,comment=t
        # register a procedure in gimp
        name="resize %sx%s border %smm"%(max(x,y),min(x,y),border)
        if force !="-":
            name+=" %s"%force
        if comment:
            name+=" %s"%comment
        if not autosave:
            name+=" no save"
        name=name.replace(".","").replace("-","").replace(" ","_")
        blurb="resize to %sx%s and add %smm border"%(max(x,y),min(x,y),border)
        menupath="<Image>/Image/resize and border/%s"%(name.replace("_"," "))
        # the reference of the wrapper function below
        function=globals()["scale_border_%s"%ref]
        # do the 'register()' magic into gimp
        register(name,blurb,blurb,author,copy,date,menupath,"",[],[],function)


def search_params(ref):
    "retrieve the detailed parameters from the reference of the wrapper"
    params=filter(None,table.splitlines())
    params.sort()
    try:
        line=params[ref-1]
        result=parse_line(line)
    except:
        result=None
    return result

            
def scale_border(image,drawable,ref):
    "2nd level wrapper function to launch make_border with full parameters"
    p=search_params(ref)
    if not p:
        return None
    x,y,border,bgcol,dpi,force,autosave,comment=p
    make_border(image,x,y,border,dpi,bgcol,force,autosave)


# 1st level wrapper function, registered in gimp with 'register(...)'
# not a problem if some predefined functions are not used :
def scale_border_1(image,drawable): scale_border(image,drawable,1)
def scale_border_2(image,drawable): scale_border(image,drawable,2)
def scale_border_3(image,drawable): scale_border(image,drawable,3)
def scale_border_4(image,drawable): scale_border(image,drawable,4)
def scale_border_5(image,drawable): scale_border(image,drawable,5)
def scale_border_6(image,drawable): scale_border(image,drawable,6)
def scale_border_7(image,drawable): scale_border(image,drawable,7)
def scale_border_8(image,drawable): scale_border(image,drawable,8)
def scale_border_9(image,drawable): scale_border(image,drawable,9)
def scale_border_10(image,drawable): scale_border(image,drawable,10)
def scale_border_11(image,drawable): scale_border(image,drawable,11)
def scale_border_12(image,drawable): scale_border(image,drawable,12)
def scale_border_13(image,drawable): scale_border(image,drawable,13)
def scale_border_14(image,drawable): scale_border(image,drawable,14)
def scale_border_15(image,drawable): scale_border(image,drawable,15)
def scale_border_16(image,drawable): scale_border(image,drawable,16)
def scale_border_17(image,drawable): scale_border(image,drawable,17)
def scale_border_18(image,drawable): scale_border(image,drawable,18)
def scale_border_19(image,drawable): scale_border(image,drawable,19)
def scale_border_20(image,drawable): scale_border(image,drawable,20)


# plugin initialisation

intialisation() # launch the register() calls
main()
