~ubuntu-branches/ubuntu/oneiric/phatch/oneiric

« back to all changes in this revision

Viewing changes to docs/phatch_dev.txt

  • Committer: Bazaar Package Importer
  • Author(s): Emilio Pozuelo Monfort
  • Date: 2008-02-13 23:48:47 UTC
  • Revision ID: james.westby@ubuntu.com-20080213234847-mp6vc4y88a9rz5qz
Tags: upstream-0.1
Import upstream version 0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
Phatch Contributors Guide
 
2
=========================
 
3
 
 
4
image::images/splashscreen2.png[Phatch Logo]
 
5
 
 
6
.Phatch = Photo & Batch!
 
7
**********************************************************************
 
8
Phatch is a simple to use cross-platform GUI Photo Batch Processor which handles all popular image formats and can duplicate (sub)folder hierarchies. Phatch can batch resize, rotate, rename, ... and more in minutes instead of hours or days if you do it manually. Phatch will also support a console version in the future to batch photos on webservers.
 
9
**********************************************************************
 
10
 
 
11
Introduction
 
12
------------
 
13
Welcome
 
14
~~~~~~~
 
15
Thanks for contributing to Phatch! 
 
16
 
 
17
* You might have exciting ideas how to extend the functionality with new features. The good news is that Phatch was designed from the ground up to make the development of actions as easy as possible. 
 
18
* Or you might want to write some documentation or tutorial.
 
19
 
 
20
Required Skills
 
21
~~~~~~~~~~~~~~~
 
22
Developping Action plugins
 
23
^^^^^^^^^^^^^^^^^^^^^^^^^^
 
24
To develop actions for Phatch you only need to know http://www.python.org[Python] and http://www.pythonware.com/products/pil[PIL] (Python Image Library). Although Phatch uses http://www.wxpython.org[wxPython] for its cross-platform GUI, you don't need at all wxPython to write actions. Phatch will do all the GUI work automatically for you behind the scenes, so you can fully concentrate on the image manipulation. Also don't worry about internationalisation, Phatch will take care of that too.
 
25
 
 
26
Writing documentation
 
27
^^^^^^^^^^^^^^^^^^^^^
 
28
Can you work with notepad, gedit or kate? Fine, that is all you need. The documentation is generated from plain text files, which follow the http://www.methods.co.nz/asciidoc[asciidoc] format. They can be translated afterwards in a lot of different formats, such as html and pdf. You don't need to install asciidoc to write documentation. You can send the plain text files to the Phatch development team and we will take care of it. However if you are curious, you will find instructions in this manual on how to install asciidoc on Ubuntu Feisty to generate pdfs. This might be usefull as well to install asciidoc on other operating systems.
 
29
 
 
30
Translating
 
31
^^^^^^^^^^^
 
32
* Translating the application Phatch is done through https://translations.launchpad.net/phatch/trunk/+pots/phatch[launchpad]. 
 
33
* Translating of documentation is done with plain text asciidoc files. Be sure to keep a copy of the source file on which you base your translation. This will allow you to quickly see what has changed with a diff viewer when new documentation is available for translation.
 
34
 
 
35
Developping Action Plugins
 
36
--------------------------
 
37
Getting Started: Invert
 
38
~~~~~~~~~~~~~~~~~~~~~~~
 
39
Introduction
 
40
^^^^^^^^^^^^
 
41
The best way to start is first to develop an PIL function which handles the manipulation. Afterwards you create input fields in order to pass the required parameters to the PIL function. 'Invert' is taken as a first case study as it doesn't require any parameters except for the image itself. Let's look at the source code:
 
42
 
 
43
.Invert Action Source Code
 
44
[python]
 
45
------------------------------------------------------------
 
46
from core import models 
 
47
from core.translation import _t
 
48
 
 
49
def invert(image):
 
50
    """PIL function"""
 
51
    return ImageChops.invert(image)                         <1>
 
52
    
 
53
class Action(models.Action):
 
54
    label       = _t('Invert')
 
55
    author      = 'Stani'
 
56
    email       = 'spe.stani.be@gmail.com'
 
57
    version     = '0.1'
 
58
    tags        = ['Colours']
 
59
    __doc__     = _t('Invert the colors of an image')       <2>
 
60
    
 
61
    def import_modules(self):
 
62
        global ImageChops
 
63
        import ImageChops                                   <1>
 
64
 
 
65
    def apply(self,photo,setting,cache):
 
66
        return self.apply_to_current_layer_image(photo)     <3>
 
67
        
 
68
    description = _t("""Invert the colors of the image.""") <4>
 
69
    
 
70
    #icon        = 'icon data'                              <5>
 
71
------------------------------------------------------------
 
72
 
 
73
<1> Importing Modules
 
74
<2> Defining The Action
 
75
<3> Applying The Action
 
76
<4> Describing The Action
 
77
<5> Adding An Icon
 
78
 
 
79
Importing Modules
 
80
^^^^^^^^^^^^^^^^^
 
81
For every action you need to add the first two lines which import the basic functionality for writing action plugins. The module models provide the architecture for the plugin: the `class Action` and the input fields. (As invert doesn't require any input, there are no fields here.) Every other module you need to import with the method `import_modules`, in which you declare them as global. For example to invert an image with PIL you need the `ImageChops` module.
 
82
 
 
83
NOTE: Why do modules have to be imported in a seperate method? The reason is that Phatch at startup imports all actions to extract the information it needs about the actions. If the imports would be declared normally, the startup would be delayed by maybe unneeded modules.
 
84
 
 
85
Defining The Action
 
86
^^^^^^^^^^^^^^^^^^^
 
87
You need to create a new `class Action` which is derived from `models.Action`. You need to define the action with the `label`, `author`, `email`, `version`, `tags` and `__doc__` properties. `label` and `__doc__` will appear translated in the 'Add Action' dialog box. That is why you need to mark them with the `_t` function. At the moment `tags` and `description`, which can contain a longer description than the `__doc__` one-liner, are not exposed yet.
 
88
 
 
89
Applying The Action
 
90
^^^^^^^^^^^^^^^^^^^
 
91
Internally Phatch works with 'photos'. 'Photos' consist of multiple 'layers'. Every 'layer' contains an 'image', but also has its own offset postion. Phatch doesn't expose this functionality yet, but will later support full layered photos, just like in Gimp. The hierarchy to remember is: Photo>Layer>Image. Luckily you don't have to worry about this in the beginning as Phatch provides you an easy method to apply a PIL function the current layer image: `apply_to_current_layer_image`.
 
92
 
 
93
Describing The Action
 
94
^^^^^^^^^^^^^^^^^^^^^
 
95
In the description property you can describe the action more elaborately. Use triple quotes for multi-line text.
 
96
 
 
97
Adding An Icon
 
98
^^^^^^^^^^^^^^
 
99
As in the example no specific icon was added, Phatch will use a default one. In the folder `phatch/pyWx/lib` there is an utility `img2py.py`. With the following command you can convert any image (eg.png) to a python file:
 
100
 
 
101
    $ python img2py.py fileName icon.py
 
102
 
 
103
This will generate an `icon.py` file, in which you will find the following code:
 
104
 
 
105
[python]
 
106
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
107
def getData():
 
108
    return zlib.decompress('icon data')
 
109
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
110
 
 
111
You can add now the 'icon data' to your action source file to define the icon:
 
112
 
 
113
[python]
 
114
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
115
class Action(models.Action):
 
116
    description = """Invert the colors of the image."""
 
117
    icon        = 'icon data'
 
118
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
119
 
 
120
Advanced: Drop Shadow
 
121
~~~~~~~~~~~~~~~~~~~~~
 
122
Introduction
 
123
^^^^^^^^^^^^
 
124
The following code was taken from the Python Cookbook:
 
125
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/474116[]
 
126
It demonstrates how easy it is to integrate existing PIL code into Phatch.
 
127
 
 
128
Source Code
 
129
^^^^^^^^^^^
 
130
.Shadow Action Source Code
 
131
[python]
 
132
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
133
from core import models
 
134
from core.translation import _t
 
135
 
 
136
def dropShadow(image, x_offset=5, y_offset=5, back_colour=(255,255,255,0), 
 
137
                shadow_colour=0x444444, border=8, iterations=3, 
 
138
                force_back=False, cache={}):
 
139
    """
 
140
    Add a gaussian blur drop shadow to an image.  
 
141
 
 
142
    image           - The image to overlay on top of the shadow.
 
143
    offset          - Offset of the shadow from the image as an (x,y) tuple.  
 
144
                        Can be positive or negative.
 
145
    back_colour     - Background colour behind the image.
 
146
    shadow_colour   - Shadow colour (darkness).
 
147
    border          - Width of the border around the image.  This must be wide
 
148
                        enough to account for the blurring of the shadow.
 
149
    iterations      - Number of times to apply the filter.  More iterations 
 
150
                        produce a more blurred shadow, but increase processing 
 
151
                        time.
 
152
    """
 
153
    #get info
 
154
    size        = image.size
 
155
    mode        = image.mode
 
156
       
 
157
    back        = None
 
158
            
 
159
    #assert image is RGBA
 
160
    if mode != 'RGBA':
 
161
        #create cache id
 
162
        id  = ''.join([str(x) for x in ['shadow_',size,x_offset,y_offset,
 
163
                    border, iterations,back_colour,shadow_colour]])
 
164
                    
 
165
        #look up in cache
 
166
        if cache.has_key(id):
 
167
            #retrieve from cache
 
168
            back, back_size = cache[id]
 
169
            
 
170
    if back is None:
 
171
        #size of backdrop
 
172
        back_size       = (size[0] + abs(x_offset) + 2*border,
 
173
                        size[1] + abs(y_offset) + 2*border)
 
174
 
 
175
        #create shadow mask
 
176
        if mode == 'RGBA':
 
177
            image_mask  = image.split()[-1]
 
178
            shadow      = Image.new('L',back_size,0)
 
179
        else:
 
180
            image_mask  = Image.new(mode,size,shadow_colour)
 
181
            shadow      = Image.new(mode,back_size,back_colour)
 
182
            
 
183
        shadow_left     = border + max(x_offset, 0)
 
184
        shadow_top      = border + max(y_offset, 0)
 
185
        shadow.paste(image_mask, (shadow_left, shadow_top, 
 
186
                                shadow_left + size[0], shadow_top + size[1]))
 
187
        del image_mask #free up memory
 
188
                            
 
189
        #blur shadow mask
 
190
 
 
191
        #Apply the filter to blur the edges of the shadow.  Since a small 
 
192
        #kernel is used, the filter must be applied repeatedly to get a decent 
 
193
        #blur.
 
194
        n   = 0
 
195
        while n < iterations:
 
196
            shadow = shadow.filter(ImageFilter.BLUR)
 
197
            n += 1
 
198
 
 
199
        #create back
 
200
        if mode == 'RGBA':
 
201
            back  = Image.new('RGBA',back_size,shadow_colour)
 
202
            back.putalpha(shadow)
 
203
            del shadow #free up memory
 
204
        else:
 
205
            back        = shadow
 
206
            cache[id]   = back, back_size
 
207
 
 
208
    #Paste the input image onto the shadow backdrop  
 
209
    image_left  = border - min(x_offset, 0)
 
210
    image_top   = border - min(y_offset, 0)
 
211
    if mode == 'RGBA':
 
212
        back.paste(image, (image_left, image_top),image)
 
213
        if force_back:
 
214
            mask    = back.split()[-1]
 
215
            back.paste(Image.new('RGB',back.size,back_colour),(0,0),
 
216
                ImageChops.invert(mask))
 
217
            back.putalpha(mask)
 
218
    else:
 
219
        back.paste(image, (image_left, image_top))
 
220
    
 
221
    return back
 
222
 
 
223
class Action(models.Action):
 
224
    """Drops shadow"""
 
225
    
 
226
    label       = _t('Shadow')
 
227
    author      = 'Stani'
 
228
    email       = 'spe.stani.be@gmail.com'
 
229
    version     = '0.1'
 
230
    tags        = [_t('filter')]
 
231
    __doc__     = _t('Drops a blurred shadow under a photo')
 
232
    
 
233
    def __init__(self):
 
234
        fields = models.Fields()
 
235
        
 
236
        fields[_t('Horizontal Offset')]        = models.PixelField('5')
 
237
        fields[_t('Vertical Offset')]          = models.PixelField('5')
 
238
        fields[_t('Border')]                   = models.PixelField('8')
 
239
        fields[_t('Shadow Blur')]              = models.SliderField(3,1,20)
 
240
        fields[_t('Background Colour')]        = models.ColourField('#FFFFFF')
 
241
        fields[_t('Shadow Colour')]            = models.ColourField('#444444')
 
242
        fields[_t('Force Background Colour')]  = models.BooleanField(True)
 
243
        
 
244
        super(Action,self).__init__(fields)
 
245
        
 
246
    def apply(self,photo,setting,cache):
 
247
        #get info
 
248
        info        = photo.get_info()
 
249
        #size
 
250
        x0, y0      = info['new_size']
 
251
        dpi         = info['new_dpi']
 
252
        #dpi
 
253
        parameters  = {
 
254
            'x_offset'      : self.get_field_size('Horizontal Offset',
 
255
                                info,x0,dpi),
 
256
            'y_offset'      : self.get_field_size('Vertical Offset',
 
257
                                info,x0,dpi),
 
258
            'border'        : self.get_field_size('Border', info,x0,dpi),
 
259
            'iterations'    : self.get_field_value('Shadow Blur', info,),
 
260
            'force_back'    : self.get_field_value('Force Background Colour',
 
261
                                info),
 
262
            'back_colour'   : self.get_field_value('Background Colour', info),
 
263
            'shadow_colour' : self.get_field_value('Shadow Colour', info),
 
264
            'cache'         : cache,
 
265
        }
 
266
 
 
267
        #manipulate layer
 
268
        return self.apply_to_current_layer_image(photo,dropShadow,**parameters) 
 
269
    
 
270
    def import_modules(self):
 
271
        #lazily import
 
272
        global Image, ImageChops, ImageFilter
 
273
        import Image, ImageChops, ImageFilter
 
274
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
275
 
 
276
Further Study
 
277
~~~~~~~~~~~~~
 
278
Probably looking at the source code of the actions, will teach you the most. You find all the actions in the folder `phatch/actions`
 
279
 
 
280
[icon="./images/icons/ubuntu.png"]
 
281
NOTE: If you installed Phatch on Ubuntu, probably the actions are in the folder: `/usr/lib/python2.5/site-packages/phatch/actions`
 
282
 
 
283
Writing Documentation    
 
284
---------------------
 
285
Introduction
 
286
~~~~~~~~~~~~
 
287
The mechanism used for documentation is asciidoc. You can find more information here: http://www.methods.co.nz/asciidoc/[]
 
288
 
 
289
You can send plain text files which will be translated into pdf an html. The html will be published on the Phatch website, which might include some advertisements. The pdf version will be a present to anyone who donates. If you contribute documentation, you agree with these conditions
 
290
 
 
291
Installing AsciiDoc
 
292
~~~~~~~~~~~~~~~~~~~
 
293
Ubuntu Feisty
 
294
^^^^^^^^^^^^^
 
295
You need to install these packages:
 
296
 
 
297
    $ sudo apt-get install asciidoc docbook-xml docbook-xsl source-highlight
 
298
    
 
299
To generate pdfs you will need http://xmlgraphics.apache.org/fop/[FOP], which unfortunately is not available as a package. Also we need an older version, as the current one is not compatible with the Ubuntu packages. Therefore download http://archive.apache.org/dist/xmlgraphics/fop/binaries/fop-0.20.5-bin.zip[fop-0.20.5-bin.zip].
 
300
    
 
301
Unzip this folder to where you want, for example `/opt/fop`. Make a symbolic link so fop.sh is recognized as a command:
 
302
 
 
303
    $ sudo ln -s /opt/fop/fop.sh /usr/local/bin/fop.sh
 
304
    
 
305
Add this line to fop.sh so that it can find your java version: 
 
306
 
 
307
    JAVA_HOME=/usr/lib/jvm/java-6-sun
 
308
    
 
309
Of course if you use another java-version, link to that file instead. To add support for PNG images, you need to download the JIMI library from SUN. Because of licensing issues, the JIMI image library is not included in the FOP distribution. First, download http://java.sun.com/products/jimi[JIMI]. Then unzip the folder and copy the file `JimiProClasses.zip` from the archive to {fop-install-dir}/lib/jimi-1.0.jar. Please note that FOP binary distributions are compiled with JIMI support, so there is no need for you to build FOP to add the support. If jimi-1.0.jar is installed in the right place, it will automatically be used by FOP, otherwise it will not.
 
310
 
 
311
Copy the filter (`./examples/source-highlight-filter/source-highlight-filter.conf`) to one of the standard AsciiDoc filter locations — typically `/etc/asciidoc/filters/` or `~/.asciidoc/filters/`.
 
312
 
 
313
(C) copyright 2007 http://www.stani.be[www.stani.be]