1#!/usr/local/bin/python 2# -*- coding: utf-8 -*- 3 4#Copyright (c) Manish Singh 5#javascript animation support by Joao S. O. Bueno Calligaris (2004) 6 7# Gimp-Python - allows the writing of Gimp plugins in Python. 8# Copyright (C) 2003, 2005 Manish Singh <yosh@gimp.org> 9# 10# This program is free software: you can redistribute it and/or modify 11# it under the terms of the GNU General Public License as published by 12# the Free Software Foundation; either version 3 of the License, or 13# (at your option) any later version. 14# 15# This program is distributed in the hope that it will be useful, 16# but WITHOUT ANY WARRANTY; without even the implied warranty of 17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18# GNU General Public License for more details. 19# 20# You should have received a copy of the GNU General Public License 21# along with this program. If not, see <https://www.gnu.org/licenses/>. 22 23# (c) 2003 Manish Singh. 24#"Guillotine implemented ala python, with html output 25# (based on perlotine by Seth Burgess)", 26# Modified by João S. O. Bueno Calligaris to allow dhtml animations (2005) 27 28import os 29 30from gimpfu import * 31import os.path 32 33gettext.install("gimp20-python", gimp.locale_directory, unicode=True) 34 35def pyslice(image, drawable, save_path, html_filename, 36 image_basename, image_extension, separate, 37 image_path, cellspacing, animate, skip_caps): 38 39 cellspacing = int (cellspacing) 40 41 if animate: 42 count = 0 43 drw = [] 44 #image.layers is a reversed list of the layers on the image 45 #so, count indexes from number of layers to 0. 46 for i in xrange (len (image.layers) -1, -1, -1): 47 if image.layers[i].visible: 48 drw.append(image.layers[i]) 49 count += 1 50 if count == 3: 51 break 52 53 54 vert, horz = get_guides(image) 55 56 if len(vert) == 0 and len(horz) == 0: 57 return 58 59 gimp.progress_init(_("Slice")) 60 progress_increment = 1 / ((len(horz) + 1) * (len(vert) + 1)) 61 progress = 0.0 62 63 def check_path(path): 64 path = os.path.abspath(path) 65 66 if not os.path.exists(path): 67 os.mkdir(path) 68 69 return path 70 71 save_path = check_path(save_path) 72 73 if not os.path.isdir(save_path): 74 save_path = os.path.dirname(save_path) 75 76 if separate: 77 image_relative_path = image_path 78 if not image_relative_path.endswith("/"): 79 image_relative_path += "/" 80 image_path = check_path(os.path.join(save_path, image_path)) 81 else: 82 image_relative_path = '' 83 image_path = save_path 84 85 tw = TableWriter(os.path.join(save_path, html_filename), 86 cellspacing=cellspacing, animate=animate) 87 88 top = 0 89 90 for i in range(0, len(horz) + 1): 91 if i == len(horz): 92 bottom = image.height 93 else: 94 bottom = image.get_guide_position(horz[i]) 95 96 tw.row_start() 97 98 left = 0 99 100 for j in range(0, len(vert) + 1): 101 if j == len(vert): 102 right = image.width 103 else: 104 right = image.get_guide_position(vert[j]) 105 if (skip_caps and 106 ( 107 (len(horz) >= 2 and (i == 0 or i == len(horz) )) or 108 (len(vert) >= 2 and (j == 0 or j == len(vert) )) 109 ) 110 ): 111 skip_stub = True 112 else: 113 skip_stub = False 114 115 if (not animate or skip_stub): 116 src = (image_relative_path + 117 slice (image, None, image_path, 118 image_basename, image_extension, 119 left, right, top, bottom, i, j, "")) 120 else: 121 src = [] 122 for layer, postfix in zip (drw, ("", "hover", "clicked")): 123 src.append (image_relative_path + 124 slice(image, layer, image_path, 125 image_basename, image_extension, 126 left, right, top, bottom, i, j, postfix)) 127 128 tw.cell(src, right - left, bottom - top, i, j, skip_stub) 129 130 left = right + cellspacing 131 132 progress += progress_increment 133 gimp.progress_update(progress) 134 135 tw.row_end() 136 137 top = bottom + cellspacing 138 139 tw.close() 140 141def slice(image, drawable, image_path, image_basename, image_extension, 142 left, right, top, bottom, i, j, postfix): 143 if postfix: 144 postfix = "_" + postfix 145 src = "%s_%d_%d%s.%s" % (image_basename, i, j, postfix, image_extension) 146 filename = os.path.join(image_path, src) 147 148 if not drawable: 149 temp_image = image.duplicate() 150 temp_drawable = temp_image.active_layer 151 else: 152 if image.base_type == INDEXED: 153 #gimp_layer_new_from_drawable doesn't work for indexed images. 154 #(no colormap on new images) 155 original_active = image.active_layer 156 image.active_layer = drawable 157 temp_image = image.duplicate() 158 temp_drawable = temp_image.active_layer 159 image.active_layer = original_active 160 temp_image.disable_undo() 161 #remove all layers but the intended one 162 while len (temp_image.layers) > 1: 163 if temp_image.layers[0] != temp_drawable: 164 pdb.gimp_image_remove_layer (temp_image, temp_image.layers[0]) 165 else: 166 pdb.gimp_image_remove_layer (temp_image, temp_image.layers[1]) 167 else: 168 temp_image = pdb.gimp_image_new (drawable.width, drawable.height, 169 image.base_type) 170 temp_drawable = pdb.gimp_layer_new_from_drawable (drawable, temp_image) 171 temp_image.insert_layer (temp_drawable) 172 173 temp_image.disable_undo() 174 temp_image.crop(right - left, bottom - top, left, top) 175 if image_extension == "gif" and image.base_type == RGB: 176 pdb.gimp_image_convert_indexed (temp_image, CONVERT_DITHER_NONE, 177 CONVERT_PALETTE_GENERATE, 255, 178 True, False, False) 179 if image_extension == "jpg" and image.base_type == INDEXED: 180 pdb.gimp_image_convert_rgb (temp_image) 181 182 pdb.gimp_file_save(temp_image, temp_drawable, filename, filename) 183 184 gimp.delete(temp_image) 185 return src 186 187class GuideIter: 188 def __init__(self, image): 189 self.image = image 190 self.guide = 0 191 192 def __iter__(self): 193 return iter(self.next_guide, 0) 194 195 def next_guide(self): 196 self.guide = self.image.find_next_guide(self.guide) 197 return self.guide 198 199def get_guides(image): 200 vguides = [] 201 hguides = [] 202 203 for guide in GuideIter(image): 204 orientation = image.get_guide_orientation(guide) 205 206 guide_position = image.get_guide_position(guide) 207 208 if guide_position > 0: 209 if orientation == ORIENTATION_VERTICAL: 210 if guide_position < image.width: 211 vguides.append((guide_position, guide)) 212 elif orientation == ORIENTATION_HORIZONTAL: 213 if guide_position < image.height: 214 hguides.append((guide_position, guide)) 215 216 def position_sort(x, y): 217 return cmp(x[0], y[0]) 218 219 vguides.sort(position_sort) 220 hguides.sort(position_sort) 221 222 vguides = [g[1] for g in vguides] 223 hguides = [g[1] for g in hguides] 224 225 return vguides, hguides 226 227class TableWriter: 228 def __init__(self, filename, cellpadding=0, cellspacing=0, border=0, 229 animate=False): 230 231 self.filename = filename 232 self.table_attrs = {} 233 234 #Hellraisen IE 6 doesn't support CSS for table control. 235 self.table_attrs['cellpadding'] = cellpadding 236 self.table_attrs['cellspacing'] = cellspacing 237 self.table_attrs['border'] = border 238 239 self.image_prefix = os.path.basename (filename) 240 self.image_prefix = self.image_prefix.split(".")[0] 241 self.image_prefix = self.image_prefix.replace ("-", "_") 242 self.image_prefix = self.image_prefix.replace (" ", "_") 243 244 245 if animate: 246 self.animate = True 247 self.images = [] 248 else: 249 self.animate = False 250 251 if os.path.exists (filename): 252 #The plug-in is running to overwrite a previous 253 #version of the file. This will parse the href targets already 254 #in the file to preserve them. 255 self.urls = self.parse_urls () 256 else: 257 self.urls = [] 258 259 self.url_index = 0 260 261 self.html = open(filename, 'wt') 262 self.open() 263 264 def next_url (self): 265 if self.url_index < len (self.urls): 266 self.url_index += 1 267 return self.urls [self.url_index - 1] 268 else: 269 #Default url to use in the anchor tags: 270 return ("#") 271 272 def write(self, s, vals=None): 273 if vals: 274 s = s % vals 275 276 self.html.write(s + '\n') 277 278 def open(self): 279 out = '''<!--HTML SNIPPET GENERATED BY GIMP 280 281WARNING!! This is NOT a fully valid HTML document, it is rather a piece of 282HTML generated by GIMP's py-slice plugin that should be embedded in an HTML 283or XHTML document to be valid. 284 285Replace the href targets in the anchor (<a >) for your URLS to have it working 286as a menu. 287 -->\n''' 288 out += '<table' 289 290 for attr, value in self.table_attrs.iteritems(): 291 out += ' %s="%s"' % (attr, value) 292 293 out += '>' 294 295 self.write(out) 296 297 def close(self): 298 self.write('</table>\n') 299 prefix = self.image_prefix 300 if self.animate: 301 out = """ 302<script language="javascript" type="text/javascript"> 303/* Made with GIMP */ 304 305/* Preload images: */ 306 images_%s = new Array(); 307 \n""" % prefix 308 for image in self.images: 309 for type_ in ("plain", "hover", "clicked"): 310 if image.has_key(type_): 311 image_index = ("%d_%d_%s" % 312 (image["index"][0], 313 image["index"][1], type_)) 314 out += (" images_%s[\"%s\"] = new Image();\n" % 315 (prefix, image_index)) 316 out += (" images_%s[\"%s\"].src = \"%s\";\n" % 317 (prefix, image_index, image[type_])) 318 319 out+= """ 320function exchange (image, images_array_name, event) 321 { 322 name = image.name; 323 images = eval (images_array_name); 324 325 switch (event) 326 { 327 case 0: 328 image.src = images[name + "_plain"].src; 329 break; 330 case 1: 331 image.src = images[name + "_hover"].src; 332 break; 333 case 2: 334 image.src = images[name + "_clicked"].src; 335 break; 336 case 3: 337 image.src = images[name + "_hover"].src; 338 break; 339 } 340 341 } 342</script> 343<!-- 344End of the part generated by GIMP 345--> 346""" 347 self.write (out) 348 349 350 def row_start(self): 351 self.write(' <tr>') 352 353 def row_end(self): 354 self.write('</tr>\n') 355 356 def cell(self, src, width, height, row=0, col=0, skip_stub = False): 357 if isinstance (src, list): 358 prefix = "images_%s" % self.image_prefix 359 self.images.append ({"index" : (row, col), "plain" : src[0]}) 360 361 out = (' <td><a href="%s"><img alt="" src="%s" ' + 362 'style="width: %dpx; height: %dpx; border-width: 0px" \n') %\ 363 (self.next_url(), src[0], width, height) 364 out += 'name="%d_%d" \n' % (row, col) 365 if len(src) >= 2: 366 self.images[-1]["hover"] = src [1] 367 out += """ onmouseout="exchange(this, '%s', 0);"\n""" % \ 368 prefix 369 out += """ onmouseover="exchange(this, '%s', 1);"\n""" % \ 370 prefix 371 if len(src) >= 3: 372 self.images[-1]["clicked"] = src [2] 373 out += """ onmousedown="exchange(this, '%s', 2);"\n""" % \ 374 prefix 375 out += """ onmouseup="exchange(this, '%s', 3);"\n""" % \ 376 prefix 377 378 379 380 out += "/></a></td>\n" 381 382 else: 383 if skip_stub: 384 out = (' <td><img alt=" " src="%s" style="width: %dpx; ' + 385 ' height: %dpx; border-width: 0px;"></td>') % \ 386 (src, width, height) 387 else: 388 out = (' <td><a href="#"><img alt=" " src="%s" ' + 389 ' style="width: %dpx; height: %dpx; border-width: 0px;">' + 390 '</a></td>') % (src, width, height) 391 self.write(out) 392 def parse_urls (self): 393 """ 394 This will parse any url targets in the href="XX" fields 395 of the given file and return then as a list 396 """ 397 import re 398 url_list = [] 399 try: 400 html_file = open (self.filename) 401 402 # Regular expression to pick everything up to the next 403 # doublequote character after finding the sequence 'href="'. 404 # The found sequences will be returned as a list by the 405 # "findall" method. 406 expr = re.compile (r"""href\=\"([^\"]*?)\"""") 407 url_list = expr.findall (html_file.read (2 ** 18)) 408 html_file.close() 409 410 except: 411 # silently ignore any errors parsing this. The file being 412 # overwritten may not be a file created by py-slice. 413 pass 414 415 return url_list 416 417 418register( 419 "python-fu-slice", 420 # table snippet means a small piece of HTML code here 421 N_("Cuts an image along its guides, creates images and a HTML table snippet"), 422 """Add guides to an image. Then run this. It will cut along the guides, 423 and give you the html to reassemble the resulting images. If you 424 choose to generate javascript for onmouseover and clicked events, it 425 will use the lower three visible layers on the image for normal, 426 onmouseover and clicked states, in that order. If skip caps is 427 enabled, table cells on the edge of the table won't become animated, 428 and its images will be taken from the active layer.""", 429 "Manish Singh", 430 "Manish Singh", 431 "2003", 432 _("_Slice..."), 433 "*", 434 [ 435 (PF_IMAGE, "image", "Input image", None), 436 (PF_DRAWABLE, "drawable", "Input drawable", None), 437 (PF_DIRNAME, "save-path", _("Path for HTML export"), os.getcwd()), 438 (PF_STRING, "html-filename", _("Filename for export"), "slice.html"), 439 (PF_STRING, "image-basename", _("Image name prefix"), "slice"), 440 (PF_RADIO, "image-extension", _("Image format"), "gif", (("gif", "gif"), ("jpg", "jpg"), ("png", "png"))), 441 (PF_TOGGLE, "separate-image-dir", _("Separate image folder"), 442 False), 443 (PF_STRING, "relative-image-path", _("Folder for image export"), "images"), 444 (PF_SPINNER, "cellspacing", _("Space between table elements"), 0, 445 (0,15,1)), 446 (PF_TOGGLE, "animate", _("Javascript for onmouseover and clicked"), 447 False), 448 # table caps are table cells on the edge of the table 449 (PF_TOGGLE, "skip-caps", _("Skip animation for table caps"), True) 450 ], 451 [], 452 pyslice, 453 menu="<Image>/Filters/Web", 454 domain=("gimp20-python", gimp.locale_directory) 455 ) 456 457main() 458