libreflow.extensions.sk.export-psd-layers 1.1.7__tar.gz → 1.1.8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (17) hide show
  1. {libreflow_extensions_sk_export_psd_layers-1.1.7 → libreflow_extensions_sk_export_psd_layers-1.1.8}/CHANGELOG.md +8 -0
  2. {libreflow_extensions_sk_export_psd_layers-1.1.7/src/libreflow.extensions.sk.export_psd_layers.egg-info → libreflow_extensions_sk_export_psd_layers-1.1.8}/PKG-INFO +9 -1
  3. {libreflow_extensions_sk_export_psd_layers-1.1.7 → libreflow_extensions_sk_export_psd_layers-1.1.8}/src/libreflow/extensions/sk/export_psd_layers/__init__.py +154 -61
  4. {libreflow_extensions_sk_export_psd_layers-1.1.7 → libreflow_extensions_sk_export_psd_layers-1.1.8}/src/libreflow/extensions/sk/export_psd_layers/_version.py +3 -3
  5. {libreflow_extensions_sk_export_psd_layers-1.1.7 → libreflow_extensions_sk_export_psd_layers-1.1.8/src/libreflow.extensions.sk.export_psd_layers.egg-info}/PKG-INFO +9 -1
  6. {libreflow_extensions_sk_export_psd_layers-1.1.7 → libreflow_extensions_sk_export_psd_layers-1.1.8}/MANIFEST.in +0 -0
  7. {libreflow_extensions_sk_export_psd_layers-1.1.7 → libreflow_extensions_sk_export_psd_layers-1.1.8}/README.md +0 -0
  8. {libreflow_extensions_sk_export_psd_layers-1.1.7 → libreflow_extensions_sk_export_psd_layers-1.1.8}/setup.cfg +0 -0
  9. {libreflow_extensions_sk_export_psd_layers-1.1.7 → libreflow_extensions_sk_export_psd_layers-1.1.8}/setup.py +0 -0
  10. {libreflow_extensions_sk_export_psd_layers-1.1.7 → libreflow_extensions_sk_export_psd_layers-1.1.8}/src/libreflow/__init__.py +0 -0
  11. {libreflow_extensions_sk_export_psd_layers-1.1.7 → libreflow_extensions_sk_export_psd_layers-1.1.8}/src/libreflow/extensions/__init__.py +0 -0
  12. {libreflow_extensions_sk_export_psd_layers-1.1.7 → libreflow_extensions_sk_export_psd_layers-1.1.8}/src/libreflow/extensions/sk/__init__.py +0 -0
  13. {libreflow_extensions_sk_export_psd_layers-1.1.7 → libreflow_extensions_sk_export_psd_layers-1.1.8}/src/libreflow.extensions.sk.export_psd_layers.egg-info/SOURCES.txt +0 -0
  14. {libreflow_extensions_sk_export_psd_layers-1.1.7 → libreflow_extensions_sk_export_psd_layers-1.1.8}/src/libreflow.extensions.sk.export_psd_layers.egg-info/dependency_links.txt +0 -0
  15. {libreflow_extensions_sk_export_psd_layers-1.1.7 → libreflow_extensions_sk_export_psd_layers-1.1.8}/src/libreflow.extensions.sk.export_psd_layers.egg-info/requires.txt +0 -0
  16. {libreflow_extensions_sk_export_psd_layers-1.1.7 → libreflow_extensions_sk_export_psd_layers-1.1.8}/src/libreflow.extensions.sk.export_psd_layers.egg-info/top_level.txt +0 -0
  17. {libreflow_extensions_sk_export_psd_layers-1.1.7 → libreflow_extensions_sk_export_psd_layers-1.1.8}/versioneer.py +0 -0
@@ -19,6 +19,14 @@ Types of changes
19
19
 
20
20
  ## [Unreleased]
21
21
 
22
+ ## [1.1.8] - 2025-10-07
23
+
24
+ ### Fixed
25
+
26
+ * Export resized to psd canvas - warning message for layers with a bouding box twice as large as the viewbox
27
+
28
+ ## [1.1.7] - 2025-08-19
29
+
22
30
  ### Added
23
31
 
24
32
  * Warning message when publishing regarding resolution and colour depth.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: libreflow.extensions.sk.export_psd_layers
3
- Version: 1.1.7
3
+ Version: 1.1.8
4
4
  Home-page: https://gitlab.com/lfs.coop/libreflow/libreflow_launcher
5
5
  Author: Thomas Thiebaut
6
6
  Author-email: autor@les-fees-speciales.coop
@@ -50,6 +50,14 @@ Types of changes
50
50
 
51
51
  ## [Unreleased]
52
52
 
53
+ ## [1.1.8] - 2025-10-07
54
+
55
+ ### Fixed
56
+
57
+ * Export resized to psd canvas - warning message for layers with a bouding box twice as large as the viewbox
58
+
59
+ ## [1.1.7] - 2025-08-19
60
+
53
61
  ### Added
54
62
 
55
63
  * Warning message when publishing regarding resolution and colour depth.
@@ -1,30 +1,26 @@
1
+ import gc
2
+ import json
1
3
  import os
2
4
  import re
3
- import json
4
- import gazu
5
5
  import time
6
6
 
7
+ import gazu
7
8
  from kabaret import flow
8
- from libreflow.flows.default.flow.film import Film
9
9
  from libreflow.baseflow.file import (
10
- GenericRunAction,
11
- TrackedFile,
10
+ CreateWorkingCopyAction,
12
11
  FileRevisionNameChoiceValue,
13
- UploadPNGToKitsu,
14
12
  PublishAndRenderPlayblast,
15
13
  PublishFileAction,
16
- UploadAfterPublishValue,
17
- CreateWorkingCopyAction,
14
+ TrackedFile,
15
+ UploadPNGToKitsu,
18
16
  )
17
+ from libreflow.flows.default.flow.film import Film
19
18
  from psd_tools import PSDImage
20
- from psd_tools.psd import PSD
21
-
22
- from libreflow.baseflow.task import Task
23
- from libreflow.baseflow.task_manager import CreateTaskDefaultFiles
24
- from libreflow.baseflow.users import PresetValue, PresetSessionValue, PresetChoiceValue
25
19
 
26
20
 
27
21
  class ExportPSDLayers(flow.Action):
22
+ """Exports Photoshop (PSD/PSB) layers for background layout and color tasks."""
23
+
28
24
  ICON = ("icons.flow", "photoshop")
29
25
 
30
26
  _file = flow.Parent()
@@ -36,6 +32,15 @@ class ExportPSDLayers(flow.Action):
36
32
  revision = flow.Param(None, FileRevisionNameChoiceValue)
37
33
 
38
34
  def allow_context(self, context):
35
+ """Check whether the given context is valid for running the action.
36
+
37
+ Args:
38
+ context: Context object, usually representing the current project/task.
39
+
40
+ Returns:
41
+ bool: True if the action can be executed in this context, False otherwise.
42
+
43
+ """
39
44
  return (
40
45
  context
41
46
  and self._file.format.get() in ["psd", "psb"]
@@ -43,24 +48,45 @@ class ExportPSDLayers(flow.Action):
43
48
  self._file.get_revision_names(
44
49
  sync_status="Available", published_only=True
45
50
  )
46
- ) > 0
51
+ )
52
+ > 0
47
53
  )
48
54
 
49
55
  def needs_dialog(self):
56
+ """Indicate whether this action requires a dialog to be displayed.
57
+
58
+ Returns:
59
+ bool: always True.
60
+
61
+ """
50
62
  msg = ""
51
63
  if self._task.name() == "bg_layout":
52
64
  msg = "<b>Layout : Photoshop Project layers will be exported separately</b>"
53
65
  elif self._task.name() == "bg_color":
54
- msg = "<b>BG Color : Photoshop Project will be exported as a single image</b>"
55
-
66
+ msg = (
67
+ "<b>BG Color : Photoshop Project will be exported as a single image</b>"
68
+ )
69
+
56
70
  self.message.set(msg)
57
71
  return True
58
72
 
59
73
  def get_buttons(self):
74
+ """Return the buttons displayed in the dialog.
75
+
76
+ Returns:
77
+ list[str]: list of button labels, typically ['Export', 'Cancel'].
78
+
79
+ """
60
80
  self.revision.revert_to_default()
61
81
  return ["Export", "Cancel"]
62
82
 
63
83
  def ensure_render_folder(self):
84
+ """Ensure the render output folder exists for the current file.
85
+
86
+ Returns:
87
+ flow.File: The folder object where rendered files will be stored.
88
+
89
+ """
64
90
  folder_name = self._file.complete_name.get()
65
91
  folder_name += "_render"
66
92
 
@@ -73,6 +99,12 @@ class ExportPSDLayers(flow.Action):
73
99
  return self._files[folder_name]
74
100
 
75
101
  def ensure_render_folder_revision(self):
102
+ """Ensure the render folder has the correct revision.
103
+
104
+ Returns:
105
+ flow.Revision: The revision object in the render folder.
106
+
107
+ """
76
108
  folder = self.ensure_render_folder()
77
109
  revision_name = self.revision.get()
78
110
  source_revision = self._file.get_revision(self.revision.get())
@@ -90,37 +122,55 @@ class ExportPSDLayers(flow.Action):
90
122
  self._files.touch()
91
123
 
92
124
  return revision
93
-
125
+
94
126
  def get_default_file(self):
127
+ """Retrieve the default file for exporting a BG color image.
128
+
129
+ Returns:
130
+ flow.File or None: The default file object, or None if not found.
131
+
132
+ """
95
133
  mng = self.root().project().get_task_manager()
96
134
  default_files = mng.default_files.get()
97
135
  for file_name, task_names in default_files.items():
98
- if 'bg_color.png' in file_name:
136
+ if "bg_color.png" in file_name:
99
137
  task = default_files[file_name][0]
100
- file_mapped_name = file_name.replace(".", '_')
138
+ file_mapped_name = file_name.replace(".", "_")
101
139
  break
102
140
 
103
141
  dft_task = mng.default_tasks[task]
104
- if not dft_task.files.has_mapped_name(file_mapped_name): # check default file
142
+ if not dft_task.files.has_mapped_name(file_mapped_name): # check default file
105
143
  # print(f'Scene Builder - default task {task_name} has no default file {filename} -> use default template')
106
144
  return None
107
145
 
108
146
  dft_file = dft_task.files[file_mapped_name]
109
147
  return dft_file
110
-
148
+
111
149
  def _ensure_file(self, name, format, path_format, source_revision):
150
+ """Ensure a file exists in the project with the given name and revision.
151
+
152
+ Args:
153
+ name (str): The base name of the file.
154
+ format (str): The file extension (e.g., "png").
155
+ path_format (str): The default path format for the file.
156
+ source_revision (flow.Revision): The source revision to copy comments from.
157
+
158
+ Returns:
159
+ flow.Revision: The revision object created or retrieved.
160
+
161
+ """
112
162
  mapped_name = "%s_%s" % (name, format)
113
163
 
114
164
  file = None
115
-
165
+
116
166
  if not self._files.has_mapped_name(mapped_name):
117
167
  if format:
118
168
  file = self._files.add_file(
119
- name=name,
120
- extension=format,
121
- tracked=True,
122
- default_path_format=path_format,
123
- )
169
+ name=name,
170
+ extension=format,
171
+ tracked=True,
172
+ default_path_format=path_format,
173
+ )
124
174
  else:
125
175
  file = self._files.add_folder(name, tracked=True)
126
176
  else:
@@ -135,7 +185,7 @@ class ExportPSDLayers(flow.Action):
135
185
  r = file.get_revision(revision_name)
136
186
 
137
187
  r.comment.set(source_revision.comment.get())
138
-
188
+
139
189
  file.ensure_last_revision_oid()
140
190
 
141
191
  r.set_sync_status("Available")
@@ -148,13 +198,22 @@ class ExportPSDLayers(flow.Action):
148
198
  os.remove(img_path)
149
199
 
150
200
  self._files.touch()
151
-
201
+
152
202
  return r
153
203
 
154
204
  def run(self, button):
205
+ """Execute the render action.
206
+
207
+ Args:
208
+ button (str): The label of the button pressed by the user (e.g., 'Export' or 'Cancel').
209
+
210
+ Returns:
211
+ Any: the result of the parent run method if executed, or None if canceled.
212
+
213
+ """
155
214
  if button == "Cancel":
156
215
  return
157
-
216
+
158
217
  session = self.root().session()
159
218
  log_format = "[EXPORT LAYERS] {message}"
160
219
 
@@ -171,23 +230,22 @@ class ExportPSDLayers(flow.Action):
171
230
  # Open photoshop file
172
231
  psb = PSDImage.open(source_revision.get_path())
173
232
 
174
-
175
233
  ############# BG LAYOUT PROCESS #############
176
234
 
177
- if self._task.name() == "bg_layout" :
235
+ if self._task.name() == "bg_layout":
178
236
 
179
237
  render_revision = self.ensure_render_folder_revision()
180
238
  # JSON structure for layers order
181
239
  layers_data = {
182
240
  "from": os.path.basename(source_revision.get_path()),
183
241
  "layers": [],
184
- "hidden_layers": []
242
+ "hidden_layers": [],
185
243
  }
186
244
 
187
245
  # Export image layers
188
246
  if os.path.exists(render_revision.get_path()) is False:
189
247
  os.makedirs(render_revision.get_path())
190
-
248
+
191
249
  folder_name = os.path.basename(render_revision.get_path())
192
250
 
193
251
  # frame_bbox = (0, 0, 0, 0)
@@ -209,22 +267,49 @@ class ExportPSDLayers(flow.Action):
209
267
  # # frame_bbox[2] - 5,
210
268
  # # frame_bbox[3] - 5,
211
269
  # # )
212
-
270
+
213
271
  # print(frame_bbox)
214
272
 
273
+ # Stop exporting action when find layers with a bouding box twice as large as the viewbox
274
+ layers_to_crop = []
275
+ for descendant in psb.descendants():
276
+ if not descendant.is_group():
277
+ v_left, v_top, v_right, v_bottom = psb.viewbox
278
+ v_width, v_height = v_right - v_left, v_bottom - v_top
279
+
280
+ bbox_left, bbox_top, bbox_right, bbox_bottom = descendant.bbox
281
+ bbox_width, bbox_height = (
282
+ bbox_right - bbox_left,
283
+ bbox_bottom - bbox_top,
284
+ )
285
+
286
+ if not (bbox_height <= v_height * 2 and bbox_width <= v_width * 2):
287
+ layers_to_crop.append(descendant.name)
288
+
289
+ if len(layers_to_crop) != 0:
290
+ session.log_warning(
291
+ log_format.format(
292
+ message=f"You need to crop these layers : {layers_to_crop}"
293
+ )
294
+ )
295
+ return None
296
+
215
297
  for layer in reversed(psb):
298
+
216
299
  # Remove invalid characters
217
- layer_name = layer.name.replace(' ','-')
300
+ layer_name = layer.name.replace(" ", "-")
218
301
  match_invalid = re.search(r"[~\"#%&*:<>?/\\{|}]+", layer.name)
219
302
  if match_invalid:
220
- layer_name = layer_name.replace(match_invalid.group(0), '')
303
+ layer_name = layer_name.replace(match_invalid.group(0), "")
221
304
 
222
305
  output_path = os.path.join(
223
306
  render_revision.get_path(),
224
307
  "{folder}-{layer}.png".format(folder=folder_name, layer=layer_name),
225
308
  )
226
309
 
227
- session.log_info(log_format.format(message=f'Exporting layer {layer_name}'))
310
+ session.log_info(
311
+ log_format.format(message=f"Exporting layer {layer_name}")
312
+ )
228
313
 
229
314
  if not layer.visible:
230
315
  layer.visible = True
@@ -235,59 +320,67 @@ class ExportPSDLayers(flow.Action):
235
320
  layer_name
236
321
  )
237
322
 
238
- image = layer.composite(viewport=psb.viewbox)
323
+ image = layer.composite(viewport=psb.viewbox, force=True)
239
324
  image.save(output_path)
325
+ session.log_info(f"Layer {layer_name} exported !")
326
+ gc.collect()
240
327
 
241
-
242
328
  # Export JSON data
243
329
  json_object = json.dumps(layers_data)
244
330
  json_path = os.path.join(render_revision.get_path(), "layers.json")
245
-
246
- session.log_info(log_format.format(message='Saving layers.json'))
331
+
332
+ session.log_info(log_format.format(message="Saving layers.json"))
247
333
  with open(json_path, "w") as outfile:
248
334
  outfile.write(json_object)
249
-
250
- session.log_info(log_format.format(message='Export complete'))
251
-
335
+
336
+ session.log_info(log_format.format(message="Export complete"))
337
+
252
338
  ############# BG COLOR PROCESS #############
253
-
254
- if self._task.name() == "bg_color" :
339
+
340
+ if self._task.name() == "bg_color":
255
341
 
256
342
  default_file = self.get_default_file()
257
343
 
258
344
  if default_file is not None:
259
-
345
+
260
346
  render_revision = self._ensure_file(
261
- name="bg_color",
262
- format="png",
263
- path_format=default_file.path_format.get(),
264
- source_revision=source_revision
265
- )
347
+ name="bg_color",
348
+ format="png",
349
+ path_format=default_file.path_format.get(),
350
+ source_revision=source_revision,
351
+ )
266
352
 
267
353
  output_path = render_revision.get_path()
268
354
 
269
355
  info_layer = psb.find("INFO")
270
- if info_layer : info_layer.visible = False
356
+ if info_layer:
357
+ info_layer.visible = False
271
358
 
272
359
  LO_layer = psb.find("REF_LAYOUT")
273
- if LO_layer : LO_layer.visible = False
360
+ if LO_layer:
361
+ LO_layer.visible = False
274
362
 
275
363
  DES_layer = psb.find("REF_DESIGN")
276
- if DES_layer : DES_layer.visible = False
364
+ if DES_layer:
365
+ DES_layer.visible = False
277
366
 
278
367
  chara_layer = psb.find("character")
279
- if chara_layer : chara_layer.visible = False
368
+ if chara_layer:
369
+ chara_layer.visible = False
280
370
 
281
371
  utils_layer = psb.find("_utils")
282
- if utils_layer : utils_layer.visible = False
372
+ if utils_layer:
373
+ utils_layer.visible = False
283
374
 
284
- image = psb.composite(viewport=psb.viewbox,force=True)
375
+ image = psb.composite(viewport=psb.viewbox, force=True)
285
376
  image.save(output_path)
286
377
 
287
- session.log_info(log_format.format(message='Export complete'))
378
+ session.log_info(log_format.format(message="Export complete"))
288
379
 
289
380
  else:
290
- self.root().session().log_error("[Export PSD] BG Color Image default file do not exist")
381
+ self.root().session().log_error(
382
+ "[Export PSD] BG Color Image default file do not exist"
383
+ )
291
384
 
292
385
  return self.get_result(close=True)
293
386
 
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2025-08-19T09:27:05+0000",
11
+ "date": "2025-10-07T12:13:47+0200",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "b1b744a86ee00adf6e955054c0c0088b9bce95a0",
15
- "version": "1.1.7"
14
+ "full-revisionid": "9b4d99a24c5df5c15e21e86f38c101e653930ad1",
15
+ "version": "1.1.8"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: libreflow.extensions.sk.export_psd_layers
3
- Version: 1.1.7
3
+ Version: 1.1.8
4
4
  Home-page: https://gitlab.com/lfs.coop/libreflow/libreflow_launcher
5
5
  Author: Thomas Thiebaut
6
6
  Author-email: autor@les-fees-speciales.coop
@@ -50,6 +50,14 @@ Types of changes
50
50
 
51
51
  ## [Unreleased]
52
52
 
53
+ ## [1.1.8] - 2025-10-07
54
+
55
+ ### Fixed
56
+
57
+ * Export resized to psd canvas - warning message for layers with a bouding box twice as large as the viewbox
58
+
59
+ ## [1.1.7] - 2025-08-19
60
+
53
61
  ### Added
54
62
 
55
63
  * Warning message when publishing regarding resolution and colour depth.