-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathartscript2.tcl
executable file
·3598 lines (3153 loc) · 125 KB
/
artscript2.tcl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#! /usr/bin/env wish
#
# ---------------:::: ArtscriptTk ::::-----------------------
# Version: v2.2.2
# Author:IvanYossi / http://colorathis.wordpress.com [email protected]
# Script inspired by David Revoy artscript / www.davidrevoy.com [email protected]
# License: GPLv3
# -----------------------------------------------------------
# Goal : Aid in the deploy of digital artwork for media with the best possible quality
# Dependencies: >=imagemagick-6.7.5, tk 8.5 zip
# Optional deps: krita, inkscape, gimp
#
# Customize:__
# Make a config file (rename presets.config.example to presets.config)
# File must be in the same directory as the script.
#
# ---------------------::::::::::::::------------------------
set ::version "v2.2.2"
package require Tk
package require platform
package require msgcat
namespace import ::msgcat::mc
proc setArtscriptDirs {} {
set home [file normalize ~]
switch -glob -- [platform::identify] {
macosx* { set ::artscript(platform) osx }
windows* { set ::artscript(platform) win }
default { set ::artscript(platform) linux }
}
switch -- $::artscript(platform) {
osx -
linux {
set agent_dirs [dict create \
home $home \
config [file join $home .config artscript] \
thumb_normal [file join $home .thumbnails normal] \
thumb_large [file join $home .thumbnails large] \
tmp [file join / tmp] \
]
}
windows {}
}
set ::artscript(home) [dict get $agent_dirs home]
set ::artscript(config) [dict get $agent_dirs config]
set ::artscript(tmp) [dict get $agent_dirs tmp]
set ::artscript(thumb_normal) [dict get $agent_dirs thumb_normal]
set ::artscript(thumb_large) [dict get $agent_dirs thumb_large]
# If folder does not exists, create it
foreach thumb_dir {thumb_normal thumb_large} {
if { ![file exists $::artscript($thumb_dir)] } {
file mkdir $::artscript($thumb_dir)
}
}
# get current running dir for lib
set ::artscript(dir) [file dirname [file normalize [info script]]]
set ::artscript(lib) [file join $::artscript(dir) lib]
}
setArtscriptDirs
lappend auto_path $::artscript(lib)
if {[info exist ::env(LANG)]} {
::msgcat::mclocale $::env(LANG)
}
::msgcat::mcload [file join $::artscript(dir) msg]
catch {package require md5}
#Set default theme to clam if supported
if { [catch {ttk::style theme use aqua}] } {
ttk::style theme use clam
}
# Do not show .dot files by default.
if { $::artscript(platform) ne {osx} } {
catch { tk_getOpenFile foo bar }
set ::tk::dialog::file::showHiddenVar 0
set ::tk::dialog::file::showHiddenBtn 1
}
namespace eval img { }
proc tkpngLoad {args} {
set load 0
if {[catch {package require tkpng}] && ($::artscript(platform) eq {linux})} {
set tkpng_dir [file join $::artscript(lib) tkpng0.9]
# check if file is writable before attempting to make link
if {![file writable $tkpng_dir]} { return }
set sys_id [split [platform::identify] {-}]
if {[string match *64 [lindex $sys_id end]]} {
file link -symbolic [file join $tkpng_dir libtkpng0.9.so] [file join $tkpng_dir libtkpng_x64-0.9.so]
} else {
file link -symbolic [file join $tkpng_dir libtkpng0.9.so] [file join $tkpng_dir libtkpng_x86-0.9.so]
}
if {[file exists [file join $tkpng_dir libtkpng0.9.so] ]} {
package require tkpng
set load 1
}
} else {
set load 1
}
if {$load} {
puts [mc "Tk png enabled"]
return 1
}
}
# TkDND module lookup
proc tkdndLoad {} {
if {[catch {package require tkdnd}]} {
set tkdnd_dir [file join $::artscript(lib) tkdnd]
source [file join $tkdnd_dir "tkdnd.tcl"]
foreach dll [glob -type f [file join $tkdnd_dir *tkdnd*[info sharedlibextension]] ] {
catch{ tkdnd::initialise $tkdnd_dir [file tail $dll] tkdnd}
}
}
puts [mc "Tk drag and drop enabled"]
foreach {type} {DND_Text DND_Files } {
::tkdnd::drop_target register .f2 $type
bind .f2 <<Drop:$type>> { listValidate %D }
}
return -code ok
}
# lambda template
proc lambda {params body} {
list apply [list $params $body]
}
# Declare default values for setting vars
proc artscriptSettings {} {
# Date values
set now [split [clock format [clock seconds] -format %Y/%m/%d/%u] "/"]
lassign $now ::year ::month ::day
set ::date [join [list $::year $::month $::day] "-"]
#--==== Artscript Default Settings
set mis_settings [dict create \
ext ".ai .bmp .dng .exr .gif .jpeg .jpg .kra .miff .ora .png .psd .svg .tga .tif .xcf .xpm .webp" \
autor "Autor" \
columns_only_show [list id ext name size output osize] \
]
# Watermark options
set wat_settings [dict create \
watermark_text {} \
watermark_text_list [list {Copyright (c) $::autor} {http://www.yourwebsite.com} {Artwork: $::autor} {$::date}] \
watermark_text_size 10 \
artscript(watermark_color) "#000000" \
watermark_text_opacity 80 \
watermark_text_position [mc "BottomRight"] \
watermark_image_list [dict create ] \
watermark_image_position [mc "Center"] \
watermark_image_size "0" \
watermark_image_style "Over" \
watermark_image_opacity 100 \
artscript(watermark_color_swatches) {} \
]
#Sizes
set siz_settings [dict create \
sizes_set([mc "wallpaper"]) [list "2560x1440" "1920x1080" "1680x1050" "1366x768" "1280x1024" "1280x720" "1024x768"] \
sizes_set([mc "percentage"]) "90% 80% 50% 25%" \
sizes_set([mc "icon"]) "128x128 96x96 48x48 36x36" \
sizes_set([mc "texture"]) "256x256 512x512" \
sizes_set([mc "default"]) "" \
]
#Collage
set col_settings [dict create \
collage_styles([mc neutral]) "bg_color grey50 border_color grey40 label_color grey30 img_color grey50" \
collage_styles([mc bright]) "bg_color grey88 border_color grey66 label_color grey30 img_color grey99" \
collage_styles([mc dark]) "bg_color grey10 border_color grey20 label_color grey50 img_color grey5" \
collage_styles([mc darker]) "bg_color grey5 border_color grey10 label_color grey45 img_color black" \
collage_layouts([mc "Photo sheet"]) "ratio 3:2 wid 275 hei 183 col 6 row 5 range 30 border 1 padding 4 label {%f: (%G)} mode {}" \
collage_layouts([mc "Storyboard"]) "ratio 16:9 wid 500 hei 281 col 3 row 3 range 9 border 1 padding 8 label {%f} mode {}" \
collage_layouts([mc "Image Strip v"]) [list ratio 4:3 wid 300 hei {} col 1 row {} range {} border 0 padding 0 label {} mode [mc "Zero geometry"]] \
collage_layouts([mc "Image Strip h"]) [list ratio 4:3 wid {} hei 300 col {} row 1 range {} border 0 padding 0 label {} mode [mc "Zero geometry"]] \
collage_layouts([mc "Square 3x3"]) "ratio 1:1 wid 350 hei 350 col 3 row 3 range {} border 0 padding 2 label {} mode {}" \
]
#Suffix and prefix ops
set suf_settings [dict create \
suffix_list [list "net" "archive" {by-[string map -nocase {{ } -} $::autor]}] \
out_prefix {} \
out_suffix {} \
]
#General arscript settings
set general_settings [dict create \
artscript(select_watermark) 0 \
artscript(select_watermark_text) 0 \
artscript(select_watermark_image) 0 \
artscript(select_size) 0 \
artscript(select_collage) 0 \
artscript(select_suffix) 0 \
artscript(overwrite) 0 \
artscript(remember_state) 0 \
artscript(global_output_folder) 0 \
artscript(output_directory) {} \
artscript(prev_directory) {} \
artscript(window_geom) {} \
artscript(alfaoff) {} \
artscript(alfa_color) "white" \
]
#Extension & output
set supported_files_string [mc "Suported Images"]
set out_settings [dict create \
out_extension "png" \
artscript(image_quality) 92 \
artscript(supported_files) [dict create \
all [list $supported_files_string [dict get $mis_settings ext] ] \
magick [list $supported_files_string {.png .jpg .jpeg .gif .bmp .miff .tif .webp} ] \
krita [list {KRA, ORA} {.ora .kra} ] \
inkscape [list {SVG, AI} {.svg .ai} ] \
gimp [list {XCF, PSD} {.xcf .psd} ] \
png [list {PNG} {.png} ] \
jpg [list {JPG, JPEG} {.jpg .jpeg} ] \
gif [list {GIF} {.gif} ] \
] \
]
#--==== END of Artscript Default Settings
set settings [dict merge $mis_settings $wat_settings $siz_settings $col_settings $suf_settings $general_settings $out_settings]
dict for {key value} $settings {
set ::$key [subst $value]
}
}
# Implement alert type call for tk_messageBox
# type,icon,title,msg => string
proc alert { title msg type icon } {
tk_messageBox -type $type -icon $icon -title $title -message $msg
}
# Implement notifySend type call for tk_messageBox
# type,icon,title,msg => string
proc notifySend { type count {priority "Critical"} } {
incr count -1
set message [mc {%1$s finished %2$s images processed} $type "\n$count" ]
if {[catch {exec notify-send -i [file join $::artscript(dir) icons "artscript.gif"] -u $priority "Artscript" "$message"}]} {
tk_messageBox -type ok -icon info -title [mc "Operations Done"] -message $message
}
}
# Find program in path
# Return bool
proc validate { program {msg 1}} {
foreach place [split $::env(PATH) {:}] {
expr { [file exists [file join $place $program]] == 1 ? [return 1] : [continue] }
}
if $msg {
puts [mc "Program %s not found" $program]
}
return 0
}
# Avoids hang on first Krita processed file if no kde environment running.
# Selects Calligraconverter for older krita releases than 2.9
proc prepare_krita_environment { args } {
set run_kdeinit4 false
if {[validate pgrep]} {
if {![catch {exec pgrep kdeinit4}]} {
set run_kdeinit4 true
}
} else {
set ps_aux [exec ps aux]
foreach el [split $ps_aux \n] {
if {[string match *kdeinit4* $el] > 0 } {
set run_kdeinit4 true
break
}
}
}
if !$run_kdeinit4 {
catch {exec kdeinit4 &} ; #Do not break if kdeinit4 missing.
}
# Choose batch converter 2.8 uses calligraconverter, 2.9 krita --export
set ::artscript(kra_cmd) [list krita {$path} --export --export-filename {$outname}]
return 0
}
# Modifies iname adding suffix, prefix, size and ext.
# If destination name file exists adds a standard suffix
# iname => file string, preffix suffix sizesuffix => string to append,
# orloc => filepath: Used to return destination to orignal loc in case of tmp files (KRA,ORA)
# returns filename.ext
proc getOutputName { iname out_extension { prefix {} } { suffix {} } { sizesufix {} } } {
if {!$::artscript(select_suffix)} {
set prefix {}
set suffix {}
} else {
foreach val {prefix suffix} {
set $val [expr {[set $val] eq "%mtime" ? [clock format [file mtime $iname] -format %Y-%m-%d] : [set $val]}]
}
}
if {[string is upper [string index $out_extension 0]]} {
set out_extension [string trim [file extension $iname] {.}]
}
set dir [file normalize [file dirname $iname]]
# change dir to destination dir to allow overwrite failsafe to work
if $::artscript(global_output_folder) {
set dir $::artscript(output_directory)
}
set name [file rootname [file tail $iname]]
# Name in brackets to protect white space
set lname [concat $prefix [list $name] $suffix $sizesufix ]
append outname [join $lname "_"] "." [lindex $out_extension 0]
if {!$::artscript(overwrite)} {
set tmpname $outname
while { [file exists [file join $dir "$outname"] ] } {
set outname $tmpname
incr s
set outname [join [list [string trimright $outname ".$out_extension"] "_$s" ".[lindex $out_extension 0]"] {} ]
}
unset tmpname
}
return $outname
}
# Replace % escapes for date values
proc replaceDateEscapes { name } {
return [string map [list %% % %d $::day %m $::month %y $::year %D $::date] $name ]
}
# Parses the list $argv for (:key value) elements. breaks if string is file
# returns list
proc getUserOps { l } {
foreach f $l {
if { [file exists $f] } {
break
}
lappend el $f
}
if { [info exists el] } {
return $el
}
}
# Get image properties Size, format and path of Image
# Receives an absolute (f)ile path
# Returns dict or error if file is not supported
proc identifyFile { f } {
set identify [list identify -quiet -format {%wx%h:%m:%M:%b:%[colorspace]@@} -ping]
if { [catch {set finfo [exec {*}$identify $f] } msg ] } {
return -code break "$msg"
} else {
lassign [split [lindex [split $finfo @@] 0] ":"] size ext path fsize colorspace
set valist [list size $size ext [string tolower $ext] path $path colorspace $colorspace]
return [dict merge $valist]
}
}
# Read only the first n lines of a textFile and return the data
proc readFileHead { file_name {n 10} } {
set data_read {}
set file_path [file normalize $file_name]
set data [open $file_path {RDONLY BINARY}]
incr i
while {(-1 != [gets $data line]) && ($i <= $n )} {
append data_read $line\n
incr i
}
close $data
return $data_read
}
# Get SVG and AI Width and Height.
# Works with plain and normal svg saved from inkscape.
# returns string {widtxheight} or 0 if nothing found
proc getWidthHeightSVG { lines } {
foreach l [split $lines] {
set value [string trim [lsearch -inline -regexp -all [list $l] {^(.)*(width|height)} ] {<xapGImg/\"\\=:whidte>}]
if {[string is double -strict $value]} {
lappend size $value
}
}
if {[info exists size]} {
return [join $size {x}]
} else {
return 0
}
}
# Computes values to insert in global inputfiles dictionary
# id => uniq integer, fpath filepath, size WxH, ext .string, h string(inkscape,krita ,gimp...)
proc setDictEntries { id fpath size ext mode h {add 1}} {
dict set ::inputfiles $id [dict create \
name [file tail $fpath] \
output [getOutputName $fpath $::out_extension $::out_prefix $::out_suffix] \
size $size \
osize [getOutputSizesForTree $size 1] \
ext [string trim $ext {.}] \
path [file normalize $fpath] \
color $mode \
deleted 0 \
]
dict set ::handlers $id $h
if {$add} {
addTreevalues $::widget_name(flist) $id
}
}
# Get contents from file and parse them into Size values.
proc getOraKraSize { image_file filext } {
set size {}
switch -- $filext {
".ora" { set unzip_file {stack.xml} }
".kra" { set unzip_file {maindoc.xml} }
}
if { [catch { set zipcon [exec unzip -p $image_file $unzip_file]} msg] } {
return -code break [mc "%s is not a valid ORA/KRA" $image_file]
}
set zipkey [regexp -inline -all -- {(w|h|width|height)="([[:digit:]]*)"} $zipcon]
foreach {s val1 val2} $zipkey {
lappend size_list [list $val1 $val2]
}
lassign [lsort -decreasing -index 0 $size_list] width height
set size [join [list [lindex $width 1] [lindex $height 1]] {x}]
return $size
}
# Validates the files supplied to be Filetypes supported by script
# Search order: gimp(xcf,psd) > inkscape(svg,ai) > krita(kra,ora,xcf,psd) > allelse
# files list
proc listValidate { files {step 0} } {
# global fc
switch $step {
0 {
set ::artscript_in(count) 0
after idle [list after 0 [list listValidate $files 1]]
} 1 {
set idnumber [lindex $files $::artscript_in(count)]
incr ::artscript_in(count)
if { $idnumber eq {} } {
updateWinTitle
return
}
set i [encoding convertfrom $idnumber]
set msg {artscript_ok}
# Call itself with directory contents if arg is dir
if {[file isdirectory $i]} {
lappend files {*}[glob -nocomplain -directory $i -type f *]
set msg directory
} else {
set filext [string tolower [file extension $i] ]
if {[lsearch $::ext $filext] == -1} {
set filext {}
}
# Get initial data to validate filetype
switch -- $filext {
.xcf { binary scan [readFileHead $i 2] A14III f w h m
set colormodes [list 0 sRGB 1 Grayscale 2 Indexed]
}
.psd { binary scan [readFileHead $i 2] a4SS3SIISS f s t fo h w depth m
if {$s != 1} { set msg [mc "%s not a valid PSD file" $i] }
set colormodes [dict create 0 Bitmap 1 Grayscale 2 Indexed 3 RGB 4 CMYK 7 Multichannel 8 Duotone 9 Lab]
}
.ora -
.kra { if { ![catch {set size [getOraKraSize $i $filext]} msg]} { set msg {artscript_ok} } }
.svg { set lines [readFileHead $i 34] }
.ai { binary scan [readFileHead $i 34] a10h18a145h14a2000 f s t fo lines
if {![string match %PDF* $f]} { set msg "error PDF" }
}
{} { set msg [mc "%s file format not supported" $i] }
default { if { ![catch {set finfo [identifyFile $i ] } msg]} { set msg {artscript_ok} } }
}
}
# If msg carries error, print and skip next phase
if { $msg == "artscript_ok" } {
set mode sRGB
# Parse data into size and converter program
switch -- $filext {
.xcf -
.psd {
set handler [expr {$::hasgimp ? "g" : "k"}]
set size [format {%dx%d} $w $h]
set mode [dict get $colormodes $m]
}
.ora { set handler [expr {$::haskrita ? "k" : "g"}] }
.kra { set handler [expr {$::haskrita ? "k" : ""}]}
.svg -
.ai {
set size [getWidthHeightSVG $lines]
set handler [expr {$::hasinkscape ? "i" : ""}]
}
default {
set size [dict get $finfo size]
set ext [dict get $finfo ext]
set mode [dict get $finfo colorspace]
set handler "m"
}
}
# Confirm krita is available if not, do not add to list
if { $handler eq "k" && !$::haskrita } {
set handler {}
}
if { $size != 0 && $handler ne {} } {
setDictEntries $::fc $i $size $filext $mode $handler
}
} else {
puts $msg
}
incr ::fc
if {($::fc % 11) == 0} {
# reduce the amount of list calculation, useful for extremely long lists
updateWinTitle
}
after idle [list after 0 [list listValidate $files 1]]
}}
}
# Searchs for presets.config in script directory, parses and set values from file to global
proc getUserPresets {} {
global ops
set presets [dict create]
set configfile [file join $::artscript(dir) "presets.config"]
if { [file exists $configfile] } {
puts [mc "Configuration file found in %s" $configfile]
puts [mc "Presets config name keys changed drastically from v2.0 to 2.1 \
If your presets does not load please review presets.config.example file to check the new names."]
set File [open $configfile r]
#read each line of File and store "key=value"
while {-1 != [gets $File line]} {
set line_init [string index $line 0]
if {$line_init ne {#} && ![string is space $line_init] } {
set values [split $line "="]
if {[llength $values] < 2} { continue } ; #Do not append if list is malformed
lappend lista $values
}
}
close $File
if {![info exists lista]} { return 0 }
#declare default dictionary to add defaut config values
if {[dict exists $ops ":preset"]} {
lappend preset [dict get $ops ":preset"]
}
#iterate list and populate dictionary with values
set preset_dict "default"
foreach i $lista {
lassign $i key value
if { $key == "preset" } {
set preset_dict $value
dict set presets $preset_dict [dict create]
continue
}
dict set presets $preset_dict $key $value
}
}
return $presets
}
# Change settings values from $::presets dict key "select"
proc setUserPresets { select } {
if {$select eq {}} {
return 0
}
set preset_values [dict get $::presets $select]
array set preset $preset_values
set catalogue [artscriptWidgetCatalogue]
dict with catalogue {
set settings [dict create]
set sizes_presets [dict filter $preset_values key sizes_set*]
# catch { dict set settings sizes_selected [dict create values [dict get $sizes_presets sizes_set(default)] selected {} ]}
catch { dict set settings sets $sizes_presets }
catch { dict lappend settings sets {*}[dict filter $preset_values key collage_sty*] }
catch { dict lappend settings sets {*}[dict filter $preset_values key collage_lay*] }
catch { dict set settings img_src [dict create values $preset(watermark_image_list) selection {}] }
foreach prop_lists [list $get_values $col_styles $raw_vars [concat $variables $preset_variables] $lists collage_label]\
prop_names {get_values col_styles raw_vars variables lists entries} {
foreach prop $prop_lists {
catch {dict set settings $prop_names $prop $preset(${prop})}
}
}
}
artscriptSetWidgetValues $settings
catch {sizeTreeAddPreset default}
return
}
# Set preset dict values. remove all sizes in list to prevent overflood
proc loadUserPresets { preset } {
sizeTreeDelete [array names ::sdict]
setUserPresets $preset
}
# Returns total of files in dict except for flagged as deleted.
# get_del bool, true = get all files loaded
# returns integer
proc getFilesTotal { { get_del 0} } {
set size [dict size $::inputfiles]
if { $get_del == 1 } {
return $size
}
set deleted 0
dict for {id datas} $::inputfiles {
if {[dict get $::inputfiles $id deleted]} {
incr deleted
}
}
return [expr {$size - $deleted}]
}
proc updateWinTitle { } {
wm title . [mc {Artscript %1$s -- %2$s Files selected} $::version [getFilesTotal]]
}
# Returns a list of ids of all elements that have args string in value
# args string list (gimp inkscape, magick, krita)
proc putsHandlers {args} {
dict for {id val} $::handlers {
if {[lsearch -all $args $val] >= 0} {
lappend images $id
}
}
return [expr {[info exists images] ? $images : {}}]
}
# Shows open dialog for supported types
proc openFiles { args } {
lassign [list {all krita inkscape gimp png jpg gif} openpath 1 . files] formats path_var multiple path mode
foreach {key value} $args { set $key $value }
if {[info exists ::artscript($path_var)]} { set path $::artscript($path_var) }
foreach key $formats {
lappend types [dict get $::artscript(supported_files) $key]
}
# Get selected files and set path to file folders
if {$mode eq "files"} {
set files [tk_getOpenFile -filetypes $types -initialdir $path -multiple $multiple]
} else {
set files [tk_chooseDirectory -initialdir $path -title "Choose a directory"]
}
if { $files ne {}} {
set ::artscript($path_var) [file dirname [lindex $files 0]]
}
return $files
}
# Loads chosen file to watermark combobox
proc loadImageWatermark {w args} {
set path [openFiles formats "magick png jpg gif" path_var imagepath multiple 0]
if {$path ne {}} {
set file [file tail $path]
dict set ::watermark_image_list $file $path
set iwatermarksk [dict keys $::watermark_image_list]
$w configure -values $iwatermarksk
$w set $file
}
}
# ----=== Gui proc events ===----
# Add key values into new treeview item id
# Receives w=widget name and id= key name of global dict
proc addTreevalues { w id } {
# global inputfiles
dict with ::inputfiles $id {
set ::img::imgid$id [$w insert {} end -values [list $id $ext $name $size $output $osize $color $path]]
}
}
# Deletes the keys from tree(w), and sets deletes value to 1
# TODO Remove all entries of file type. (filtering)
proc removeTreeItem { w i } {
# TODO undo last delete
foreach item $i {
set id [$w set $item id]
dict set ::inputfiles $id deleted 1
# unset ::img::imgid$id
}
# remove keys from tree
$w detach $i
updateWinTitle
}
# from http://wiki.tcl.tk/20930
# Sorts tree values by column
proc treeSort {tree col direction} {
# Build something we can sort
set data {}
foreach row [$tree children {}] {
lappend data [list [$tree set $row $col] $row]
}
set dir [expr {$direction ? "-decreasing" : "-increasing"}]
set r -1
# Now reshuffle the rows into the sorted order
foreach info [lsort -dictionary -index 0 $dir $data] {
$tree move [lindex $info 1] {} [incr r]
}
# Switch the heading so that it will sort in the opposite direction
set cmd [list treeSort $tree $col [expr {!$direction}]]
$tree heading $col -command $cmd
}
# Sorts tree column by pair tags On/off
# tree widgetname, col, column, tag/antitag
proc treeSortTagPair {tree col tag antitag} {
# Build something we can sort
set data {}
foreach row [$tree tag has $tag] {
lappend data $row
}
set r -1
foreach info [lsort $data] {
$tree move $info {} [incr r]
}
# reverse sort order
set cmd [list treeSortTagPair $tree $col $antitag $tag]
$tree heading $col -command $cmd
}
# Updates global variable
# var = global variable name, value = new value
# TODO: check if it is still necessary
# proc updateTextLabel { var value } {
# upvar #0 $var ltext
# set ltext $value
# return
# }
#creates headers and assing visibility to them.
proc treeviewHeaderAssign { {w 0} {tree_scroll 0}} {
set header_strings [dict create id [mc "ID"] ext [mc "ext."] name [mc "Input"] size [mc "Size"] output [mc "Output"] osize [mc "Size out"] color [mc "Color Profiles"] path [mc "Location"] ]
set fileheaders [dict keys $header_strings]
if {$w != 0} {
set ::widget_name(flist) [ttk::treeview $w -columns $fileheaders -show headings -yscrollcommand "$tree_scroll set"]
$::widget_name(flist) configure -displaycolumns {0 1 2 3 4 5}
}
for {set i 0} {$i < [dict size $header_strings]} {incr i} {
set col [$::widget_name(flist) column $i -id]
$::widget_name(flist) heading $col -text [dict get $header_strings $col] -command [list treeSort $::widget_name(flist) $col 0 ]
switch -exact -- $col {
id { $::widget_name(flist) column id -width 32 -stretch 0 }
ext { $::widget_name(flist) column ext -width 48 -stretch 0 }
size { $::widget_name(flist) column size -width 86 -stretch 0 }
osize { $::widget_name(flist) column osize -width 86 }
}
if { [lsearch -exact $::columns_only_show $col] >= 0 } {
lappend column_requested $i
}
}
if { [info exist column_requested]} {
# set the order as requested.
foreach col_u $::columns_only_show {
foreach col_s $column_requested {
if { [$::widget_name(flist) column $col_s -id] == $col_u } {
lappend column_order $col_s
}
}
}
$::widget_name(flist) configure -displaycolumns $column_order
}
}
# Transform a read with the supplied script and writes it to dict and treeview
# Script: script to run, w = widget, write/read = dict key or tree column
proc treeAlterVal { {script {set $value}} w read write } {
global inputfiles
foreach id [dict keys $inputfiles] {
set value [dict get $inputfiles $id $read]
set newvalue [uplevel 0 $script]
$w set [set ::img::imgid$id] $write $newvalue
dict set inputfiles $id $write $newvalue
if { $read == "path" } {
set path [file dirname $value]
if {[file exists [file join $path "$newvalue"] ]} {
$w item [set ::img::imgid$id] -tags {exists}
} else {
$w item [set ::img::imgid$id] -tags {}
}
}
}
}
# Update output name column value only if column is available
proc treeUpdateOuputColumn {} {
treeAlterVal {getOutputName $value $::out_extension $::out_prefix $::out_suffix} $::widget_name(flist) path output
return
}
# Updates checkbox state and output name values on tree (w)
proc printOutname { w } {
if {$::artscript(select_suffix) || $w != 0} {
set ::artscript(select_suffix) 1
}
treeUpdateOuputColumn
}
# Check id of thumbnail shown sends it to convert to preview.
proc showPreview {} {
if {[info exists ::artscript(preview_id)]} {
prepConvert Convert $::artscript(preview_id) 1
}
return
}
# Read process for binary files, to avoid breakage fo binary data.
# TODO: quick fix, improve.
proc readBinaryFile { f var } {
set status [catch {append ::artscript(thumb_data) [read $f]} message]
# set status [catch { gets $f line } result]
if { $status != 0 } {
# unexpected error
puts [mc "Error! %s" $message]
closePipe $f [list after idle set $var 1]
} elseif { [eof $f] } {
fconfigure $f -blocking true
closePipe $f [list after idle set $var 1]
}
#elseif { $message >= 0 } { }
}
proc getBinaryData { script var } {
catch {close $::artscript(thumb_chan)}
set ::artscript(thumb_data) {}
set ::artscript(thumb_chan) [open "| $script 2>@1" rb]
fconfigure $::artscript(thumb_chan) -blocking false
fileevent $::artscript(thumb_chan) readable [list readBinaryFile $::artscript(thumb_chan) $var]
}
# Make Gif thumbnail (bug at fast scrubbing)
proc setThumbGIF { path } {
getBinaryData [list convert $path -strip GIF:-] ::thumb
vwait ::thumb
catch {set ::img_thumb [image create photo -data $::artscript(thumb_data)]}
thumbUpscale $path
$::widget_name(thumb-im) configure -compound image -image $::img_thumb
}
proc setThumbPNG { path } {
set ::img_thumb [image create photo -file $path]
thumbUpscale $path
$::widget_name(thumb-im) configure -compound image -image $::img_thumb
}
# doubles sizes of thumbnail if comes from normal size folder.
proc thumbUpscale { path } {
if {[lindex [file split $path] end-1] eq "normal"} {
set ::img_scale [image create photo]
$::img_scale copy $::img_thumb
$::img_thumb blank
$::img_thumb copy $::img_scale -shrink -zoom 2 2
image delete $::img_scale
}
}
# Loads the proper thumbnail into preview widget.
# w = widget, selected_f = tree item selected
proc showThumb { w selected_f } {
set f [lindex $selected_f 0]
set preview_path [dict get $::inputfiles [$::widget_name(flist) set $f id] path]
set preview_ext [string tolower [file extension $preview_path] ]
set ::artscript(preview_id) [$::widget_name(flist) set $f id]
# Get png md5sum name.
set thumb_name [string tolower [::md5::md5 -hex "file://$preview_path"]]
set thumb_normal [file join $::artscript(thumb_normal) "$thumb_name.png"]
set thumb_large [file join $::artscript(thumb_large) "$thumb_name.png"]
set thumb_Cmd [expr {$::artscript(tkpng) ? {setThumbPNG} : {setThumbGIF}}]
catch {image delete $::img_thumb}
# Loads preview from thumbs in disk if available
if {[catch {$thumb_Cmd $thumb_large}]} {
catch {$thumb_Cmd $thumb_normal}
}
getThumb $preview_path $preview_ext [string range $thumb_Cmd end-2 end]
return 0
}
# Gets or renders large thumbnail for all images, thumbnail is not saved ondisk
# path location, extension type.
proc getThumb {path ext format} {
if {[lsearch [lindex [dict get $::artscript(supported_files) krita] 1] $ext] >= 0} {
dict set type .ora {Thumbnails/thumbnail.png}
dict set type .kra {preview.png}
set Cmd [list unzip -p $path [dict get $type $ext] | convert - $format:-]
} elseif {[lsearch [lindex [dict get $::artscript(supported_files) magick] 1] $ext] >= 0} {
set Cmd [list convert $path -thumbnail 256x256 $format:-]
} else {
$::widget_name(thumb-im) configure -image {} -compound text -text [mc "No Thumbnail"]
return
}
getBinaryData $Cmd ::thumb
vwait ::thumb
catch {set ::img_thumb [image create photo -data $::artscript(thumb_data)]}
$::widget_name(thumb-im) configure -compound image -image $::img_thumb
# set ::artscript(thumb_data) {}
}
# Scroll trough tabs on a notebook. (dir = direction)
proc scrollTabs { w i {dir 1} } {
set tlist [llength [$w tabs]]
expr { $dir ? [set op "-"] : [set op ""] }
incr i ${op}1
if { $i < 0 } {
$w select [expr {$tlist-1}]
} elseif { $i == $tlist } {
$w select 0
} else {
$w select $i
}
}
# Defines combobox editable events.
proc comboBoxEditEvents { w {script {} }} {
bind $w <<ComboboxSelected>> $script
bind $w <KeyRelease> $script
foreach event {Button-3 Control-Button-1 FocusIn} {
bind $w <$event> { %W configure -state normal } ; #space
}
bind $w <FocusOut> { %W configure -state readonly; %W selection clear }
}
# Validates input for quality spinbox, has to be a positive number not bigger than.
proc validateQualitySpinbox { value } {
if {[string is integer $value]} {
if { ( ($value <= $::artscript(quality_maximum)) && ($value > 0) ) || $value eq {} } {
return 1
}
}
return 0
}
# Convert RGB to HSV, to calculate contrast colors
# Returns float list => hue, saturation, value, lightness, luma
proc rgbtohsv { r g b } {
foreach color {r g b} {
set ${color}1 [expr {[set ${color}]/255.0}]
}
set max [expr {max($r1,$g1,$b1)}]
set min [expr {min($r1,$g1,$b1)}]
set delta [expr {$max-$min}]
set h -1
set s {}
lassign [lrepeat 3 $max] v l luma
if {$delta != 0} {
set l [expr { ($max + $min) / 2 } ]
set s [expr { $delta/$v }]
set luma [expr { (0.2126 * $r1) + (0.7152 * $g1) + (0.0722 * $b1) }]
if { $max == $r1 } {
set h [expr { ($g1-$b1) / $delta }]
} elseif { $max == $g1 } {
set h [expr { 2 + ($b1-$r1) / $delta }]
} else {
set h [expr { 4 + ($r1-$g1) / $delta }]
}
set h [expr {round(60 * $h)}]
if { $h < 0 } { incr h 360 }
} else {
set s 0
}
return [list $h [format "%0.2f" $s] [format "%0.2f" $v] [format "%0.2f" $l] [format "%0.2f" $luma]]
}
# Calls tk colorchooser and sets color on canvas element widget.
# return hex color string
# TODO, remove hardcoded names to allow use on other canvas widgets
proc setColor { w item col {chooser 1} } {
set identify_tag [lindex [$w itemcget $item -tags] 0]
switch -- $identify_tag {
"bg" { set title [mc "Collage Background Color"]}
"border" { set title [mc "Collage Border Color"]}
"label" { set title [mc "Collage Label Color"]}
"watermark" { set title [mc "Watermark Text Color"]}
"default" { set title [mc "Choose color"]}
}
set col [lindex $col end]
if { $chooser } {
set col [tk_chooseColor -title $title -initialcolor $col -parent .]
}
if { $col ne "" } {
$w itemconfigure $item -fill $col
} else {
return -code break "No color selected"
}
return $col
}