├── AI2ASS UI.coffee
├── AI2ASS.coffee
├── COPYING
├── README.md
├── applyLines.moon
├── build.ps1
├── build.sh
├── built
└── AI2ASS.jsx
└── screenshot.png
/AI2ASS UI.coffee:
--------------------------------------------------------------------------------
1 | dlgRes = "Group { orientation:'column', alignChildren: ['fill', 'fill'], \
2 | output: Panel { orientation:'column', text: 'ASS Output', \
3 | edit: EditText {text: 'have ass, will typeset', properties: {multiline: true}, alignment: ['fill', 'fill'], preferredSize: [-1, 100] } \
4 | }, \
5 | outputFormat: Panel { orientation:'column', text: 'Output Format', \
6 | clip: Group {orientation: 'row', alignChildren: ['fill', 'fill'], spacing: 5, \
7 | noclip: RadioButton {text: 'Drawing', value: true}, \
8 | clip: RadioButton {text: '\\\\clip'}, \
9 | iclip: RadioButton {text: '\\\\iclip'}, \
10 | bare: RadioButton {text: 'Bare'}, \
11 | line: RadioButton {text: 'Line'} \
12 | }, \
13 | }, \
14 | settings: Panel {orientation: 'column', alignChildren: ['left','fill'], text: 'Settings', \
15 | collectionTarget: DropDownList {title: 'Collection Target:'}, \
16 | pathCombining: DropDownList {title: 'Path Combining:'} \
17 | }, \
18 | export: Button {text: 'Export'} \
19 | }"
20 |
21 |
22 | win = new Window "palette", "Export ASS" , undefined, {}
23 | dlg = win.add dlgRes
24 |
25 | outputFormats = {
26 | "Drawing:": "noclip"
27 | "\\clip": "clip"
28 | "\\iclip": "iclip"
29 | "Bare": "bare"
30 | "Line": "line"
31 | }
32 |
33 | exportMethods = {
34 | "Active Layer": "collectActiveLayer"
35 | "Non-Empty Layers": "collectAllLayers"
36 | "All Layers": "collectAllLayersIncludeEmpty"
37 | }
38 | dlg.settings.collectionTarget.add "item", k for k, v of exportMethods
39 | dlg.settings.collectionTarget.selection = 0
40 |
41 | pathCombiningStrategies = {
42 | "Disabled": "off"
43 | "Safe (Maintain Order)": "safe"
44 | "Ignore Blending Order": "any"
45 | }
46 | dlg.settings.pathCombining.add "item", k for k, v of pathCombiningStrategies
47 | dlg.settings.pathCombining.selection = 1
48 |
49 | bt = new BridgeTalk
50 | bt.target = "illustrator"
51 | backendScript = ai2assBackend.toString()
52 |
53 | radioString = ( radioGroup ) ->
54 | for child in radioGroup.children
55 | if child.value
56 | return outputFormats[child.text]
57 |
58 | objToString = (obj) ->
59 | fragments = ("#{k}: \"#{v}\"" for k, v of obj).join ", "
60 | return "{#{fragments}}"
61 |
62 | dlg.export.onClick = ->
63 | dlg.output.edit.active = false
64 | options = objToString {
65 | method: exportMethods[dlg.settings.collectionTarget.selection.text]
66 | wrapper: radioString dlg.outputFormat.clip
67 | combineStrategy: pathCombiningStrategies[dlg.settings.pathCombining.selection.text]
68 | }
69 |
70 | bt.body = "(#{backendScript})(#{options});"
71 |
72 | bt.onResult = ( result ) ->
73 | dlg.output.edit.text = result.body.replace( /\\\\/g, "\\" ).replace /\\n/g, "\n"
74 | dlg.output.edit.active = true
75 |
76 | bt.onError = ( err ) ->
77 | alert "#{err.body} (#{a.headers["Error-Code"]})"
78 |
79 | bt.send( )
80 |
81 | win.show( )
82 |
--------------------------------------------------------------------------------
/AI2ASS.coffee:
--------------------------------------------------------------------------------
1 | `#target illustrator`
2 | `#targetengine main`
3 |
4 | ai2assBackend = ( options ) ->
5 | app.userInteractionLevel = UserInteractionLevel.DISPLAYALERTS
6 | pWin = new Window "palette"
7 | pWin.text = "Progress Occurs"
8 | pWin.pBar = pWin.add "progressbar", undefined, 0, 250
9 | pWin.pBar.preferredSize = [ 250, 10 ]
10 | doc = app.activeDocument
11 | org = doc.rulerOrigin
12 | black = new RGBColor();
13 |
14 | countPathItems = ( obj ) ->
15 | recurse = ( obj ) ->
16 | unless obj.hidden
17 | switch obj.typename
18 | when "Document"
19 | recurse layer for layer in obj.layers
20 | when "Layer", "GroupItem"
21 | recurse pageItem for pageItem in obj.pageItems
22 | when "CompoundPathItem"
23 | recurse path for path in obj.pathItems
24 | when "PathItem"
25 | count += 1
26 |
27 | count = 0
28 | recurse obj
29 | return count
30 |
31 | run = (root = doc, includeEmptyLayers) ->
32 | output = {
33 | combineStrategy: "safe"
34 | layers: []
35 | pathCnt: null
36 | processedPathCnt: 0
37 | tempLayer: null
38 |
39 | makeTempLayer: (name = "AI2ASS_tmp") ->
40 | @tempLayer = doc.layers.add()
41 | @tempLayer.name = name
42 | @tempLayer.zOrder(ZOrderMethod.SENDTOBACK)
43 |
44 | makeClip: (clippingPath) ->
45 | clip = {
46 | tempGroup: null
47 | isVisible: false
48 | output: @
49 |
50 | add: (clippingPath) ->
51 | # prepare a group to apply the pathfinder effect to
52 | @output.makeTempLayer() unless @output.tempLayer?
53 |
54 | unless @tempGroup
55 | @tempGroup = @output.tempLayer.groupItems.add()
56 |
57 | # copy all path into the group and make sure it has a fill
58 | copy = clippingPath.duplicate(@tempGroup, ElementPlacement.PLACEATBEGINNING)
59 | copy.filled = true
60 | copy.stroked = false
61 | copy.clipping = false
62 | copy.fillColor = black
63 |
64 | if @tempGroup.pageItems.length > 1
65 | # select the group, apply the pathfinder and expand
66 | prevSelection = doc.selection
67 | doc.selection = [@tempGroup]
68 | app.executeMenuCommand("Live Pathfinder Intersect")
69 | app.executeMenuCommand("expandStyle")
70 | # expanding created a new group
71 | @tempGroup = doc.selection[0]
72 |
73 | # no intersection between paths means we have an empty clipping area
74 | if @tempGroup.pageItems.length == 1
75 | @isVisible = true
76 | else
77 | @isVisible = false
78 | @tempGroup.pageItems.removeAll()
79 |
80 | # restore previous selection
81 | doc.selection = prevSelection
82 | else @isVisible = true
83 |
84 | copy: -> return makeClip @tempGroup.pageItems[0]
85 | get: -> return @tempGroup.pageItems[0]
86 | getASS: ->
87 | drawing = ASS_createDrawingFromPoints @tempGroup.pageItems[0].pathPoints
88 | return "\\clip(#{drawing.join ' '})"
89 | }
90 |
91 | clip.add clippingPath
92 | return clip
93 |
94 | makeLayer: (emptyPrefix) ->
95 | layer = {
96 | groups: []
97 | currGroupIdx: -1
98 | currGroup: null
99 | emptyPrefix: null
100 |
101 | makeMergeGroup: () ->
102 | group = {
103 | dirtyRects: []
104 | lines: {}
105 | layer: @
106 |
107 | addPath: (path, prefix) ->
108 | unless @isZeroArea path.visibleBounds
109 | @dirtyRects.push path.visibleBounds
110 | drawing = ASS_createDrawingFromPoints path.pathPoints
111 |
112 | if @lines[prefix]?
113 | Array.prototype.push.apply @lines[prefix], drawing
114 | else @lines[prefix] = drawing
115 |
116 | isZeroArea: (bounds) ->
117 | return bounds[2]-bounds[0] == 0 and bounds[3]-bounds[1] == 0
118 |
119 | isMergeable: (path) ->
120 | if path.parent.typename == "CompoundPathItem"
121 | return true
122 |
123 | switch @layer.combineStrategy
124 | when "off"
125 | return false
126 | when "any"
127 | return true
128 | when "safe"
129 | bounds = path.visibleBounds
130 |
131 | if @isZeroArea bounds
132 | return true
133 |
134 | for rect in @dirtyRects
135 | if bounds[2] > rect[0] and bounds[0] < rect[2] and bounds[3] < rect[1] and bounds[1] > rect[3]
136 | return false
137 |
138 | return true
139 | }
140 |
141 | return group
142 |
143 | addGroup: ->
144 | @currGroupIdx += 1
145 | @currGroup = @makeMergeGroup()
146 | @groups[@currGroupIdx] = @currGroup
147 |
148 | addPath: (path, prefix) ->
149 | unless @currGroup.isMergeable path
150 | @addGroup()
151 | @currGroup.addPath path, prefix
152 | }
153 |
154 | layer.emptyPrefix = emptyPrefix
155 | layer.combineStrategy = @combineStrategy
156 | layer.addGroup()
157 | return layer
158 |
159 | process: ( obj, clip, opacity = 100 ) ->
160 | if not @pathCnt?
161 | @pathCnt = countPathItems obj
162 |
163 | if !obj.hidden and (not clip? or clip.isVisible)
164 | opacity = if obj.opacity? then opacity * obj.opacity/100 else 100
165 |
166 | switch obj.typename
167 | when "Document"
168 | for layer in obj.layers by -1
169 | @process layer
170 |
171 | when "Layer"
172 | if obj.pageItems.length == 0
173 | @layers[obj.zOrderPosition] = @makeLayer @emptyPrefix obj.zOrderPosition, obj.name
174 | else
175 | for subPageItem in obj.pageItems by -1
176 | @process subPageItem, null, opacity
177 |
178 | when "CompoundPathItem"
179 | for path in obj.pathItems by -1
180 | @process path, clip, opacity
181 |
182 | when "GroupItem"
183 | if obj.clipped
184 | clipPath = (pI for pI in obj.pageItems when pI.clipping)[0]
185 | if clip?
186 | clip = clip.copy()
187 | clip.add clipPath
188 | else
189 | clip = @makeClip clipPath
190 | @processedPathCnt += 1
191 |
192 | for subPageItem in obj.pageItems by -1 when not subPageItem.clipping
193 | @process subPageItem, clip, opacity
194 |
195 | when "PathItem"
196 | if @processedPathCnt % 10 == 0
197 | pWin.pBar.value = Math.ceil @processedPathCnt*250/@pathCnt
198 | pWin.update( )
199 |
200 | unless obj.guides or not (obj.stroked or obj.filled or obj.clipping) or not obj.layer.visible
201 | @appendPath obj, clip, opacity
202 |
203 | @processedPathCnt += 1
204 |
205 | appendPath: ( path, clipObj, opacity ) ->
206 | stroke = manageColor path, "strokeColor", 3
207 | fill = manageColor path, "fillColor", 1
208 | layerName = path.layer.name
209 | layerNum = path.layer.zOrderPosition
210 | alpha = manageOpacity opacity
211 | clip = if clipObj? then clipObj.getASS() else ""
212 |
213 | prefix = @prefix stroke, fill, clip, alpha, layerNum, layerName
214 |
215 | layer = @layers[layerNum]
216 | unless layer?
217 | layer = @makeLayer()
218 | @layers[layerNum] = layer
219 |
220 | layer.addPath path, prefix
221 |
222 | prefix: (stroke, fill, clip, alpha) ->
223 | "{\\an7\\pos(0,0)#{stroke}#{fill}#{alpha}#{clip}\\p1}"
224 |
225 | emptyPrefix: -> ""
226 | suffix: -> "{\\p0}"
227 |
228 | get: (includeEmptyLayers) ->
229 | fragments = []
230 | suffix = @suffix()
231 |
232 | for layer in @layers when layer?
233 | if includeEmptyLayers && layer.emptyPrefix?
234 | fragments.push layer.emptyPrefix
235 | fragments.push "\n"
236 |
237 | for mergeGroup in layer.groups
238 | for prefix, drawing of mergeGroup.lines
239 | fragments.push prefix
240 | fragments.push drawing.join " "
241 | fragments.push suffix
242 | fragments.push "\n"
243 |
244 | fragments.pop()
245 | return fragments.join ""
246 | }
247 |
248 | if options.combineStrategy?
249 | output.combineStrategy = options.combineStrategy
250 |
251 | switch options.wrapper
252 | when "clip"
253 | output.prefix = -> "\\clip("
254 | output.suffix = -> ")"
255 | when "iclip"
256 | output.prefix = -> "\\iclip("
257 | output.suffix = -> ")"
258 | when "bare"
259 | output.prefix = -> ""
260 | output.suffix = -> ""
261 | when "line"
262 | output.prefix = (stroke, fill, clip, alpha, layerNum, layerName) ->
263 | "Dialogue: #{layerNum},0:00:00.00,0:00:00.00,AI,#{layerName},0,0,0,,{\\an7\\pos(0,0)#{stroke}#{fill}#{alpha}#{clip}\\p1}"
264 | output.suffix = -> ""
265 | output.emptyPrefix = (layerNum, layerName) ->
266 | "Dialogue: #{layerNum},0:00:00.00,0:00:00.00,AI,#{layerName},0,0,0,,"
267 |
268 | alert "Your colorspace needs to be RGB if you want colors." if doc.documentColorSpace == DocumentColorSpace.CMYK
269 |
270 | pWin.show()
271 | output.process root
272 |
273 | output.tempLayer.remove() if output.tempLayer?
274 | pWin.close()
275 | return output.get(includeEmptyLayers)
276 |
277 | drawing = {
278 | commands: []
279 | new: -> @commands = []
280 | get: -> return @commands
281 |
282 | CmdTypes: {
283 | None: -1
284 | Move: 0
285 | Linear: 1
286 | Cubic: 2
287 | }
288 |
289 | prevCmdType: -1
290 |
291 | addMove: ( point ) ->
292 | @commands.push "m"
293 | @addCoords point.anchor
294 | @prevCmdType = @CmdTypes.Move
295 |
296 | addLinear: ( point ) ->
297 | if @prevCmdType != @CmdTypes.Linear
298 | @commands.push "l"
299 | @prevCmdType = @CmdTypes.Linear
300 |
301 | @commands.push
302 | @addCoords point.anchor
303 |
304 | addCubic: (currPoint, prevPoint) ->
305 | if @prevCmdType != @CmdTypes.Cubic
306 | @commands.push "b"
307 | @prevCmdType = @CmdTypes.Cubic
308 |
309 | @addCoords prevPoint.rightDirection
310 | @addCoords currPoint.leftDirection
311 | @addCoords currPoint.anchor
312 |
313 | # For ASS, the origin is the top-left corner
314 | addCoords: ( coordArr ) ->
315 | @commands.push Math.round( (coordArr[0] + org[0])*100 )/100
316 | @commands.push Math.round( (doc.height - (org[1] + coordArr[1]))*100 )/100
317 |
318 | }
319 |
320 |
321 |
322 | checkLinear = ( currPoint, prevPoint ) ->
323 | p1 = (prevPoint.anchor[0] == prevPoint.rightDirection[0] && prevPoint.anchor[1] == prevPoint.rightDirection[1])
324 | p2 = (currPoint.anchor[0] == currPoint.leftDirection[0] && currPoint.anchor[1] == currPoint.leftDirection[1])
325 | (p1 && p2)
326 |
327 | zeroPad = ( num ) ->
328 | hexStr = num.toString(16).toUpperCase()
329 | return if num < 16 then "0#{hexStr}" else hexStr
330 |
331 | handleGray = ( theColor ) ->
332 | pct = theColor.gray
333 | pct = Math.round (100-pct)*255/100
334 | "&H#{zeroPad pct}#{zeroPad pct}#{zeroPad pct}&"
335 |
336 | handleRGB = ( theColor ) ->
337 | r = Math.round theColor.red # why am I rounding these?
338 | g = Math.round theColor.green
339 | b = Math.round theColor.blue
340 | "&H#{zeroPad b}#{zeroPad g}#{zeroPad r}&"
341 |
342 | manageColor = ( currPath, field, ASSField ) ->
343 | fmt = ""
344 |
345 | switch currPath[field].typename
346 | when "RGBColor"
347 | fmt = handleRGB currPath[field]
348 | when "GrayColor"
349 | fmt = handleGray currPath[field]
350 | when "NoColor"
351 | switch field
352 | when "fillColor"
353 | return "\\#{ASSField}a&HFF&"
354 | when "strokeColor"
355 | return ""#\\bord0"
356 | else
357 | return ""
358 |
359 | "\\#{ASSField}c#{fmt}"
360 | # "GradientColor"
361 | # "LabColor"
362 | # "PatternColor"
363 | # "SpotColor"
364 |
365 | manageOpacity = (opacity) ->
366 | if opacity >= 100
367 | return ""
368 |
369 | return "\\alpha&H#{zeroPad 255 - Math.round(opacity)/100 * 255}&"
370 |
371 | ASS_createDrawingFromPoints = ( pathPoints ) ->
372 | drawing.new()
373 |
374 | if pathPoints.length > 0
375 | drawing.addMove pathPoints[0]
376 |
377 | for j in [1...pathPoints.length] by 1
378 | currPoint = pathPoints[j]
379 | prevPoint = pathPoints[j-1]
380 |
381 | if checkLinear currPoint, prevPoint
382 | drawing.addLinear currPoint
383 | else
384 | drawing.addCubic currPoint, prevPoint
385 |
386 | prevPoint = pathPoints[pathPoints.length-1]
387 | currPoint = pathPoints[0]
388 |
389 | if checkLinear currPoint, prevPoint
390 | drawing.addLinear currPoint
391 | else
392 | drawing.addCubic currPoint, prevPoint
393 |
394 | return drawing.get()
395 |
396 | methods = {
397 | collectActiveLayer: ->
398 |
399 | # PAGEITEMS DOES NOT INCLUDE SUBLAYERS, AND AS FAR AS I CAN TELL,
400 | # THERE'S NO WAY TO POSSIBLY TELL FROM JS WHAT ORDER SUBLAYERS ARE
401 | # IN RELATIVE TO THE PATHS, COMPOUND PATHS, AND GROUPS WITHIN THE
402 | # LAYER, WHICH MEANS IT IS IMPOSSIBLE TO REPRODUCE THE WAY
403 | # SUBLAYERS ARE LAYERED. TL;DR IF YOU STICK A LAYER INSIDE ANOTHER
404 | # LAYER, FUCK YOU FOREVER.
405 | currLayer = doc.activeLayer
406 |
407 | unless currLayer.visible
408 | return "Not doing anything to that invisible layer."
409 | run currLayer
410 |
411 | collectAllLayers: -> run()
412 |
413 | collectAllLayersIncludeEmpty: -> run doc, true
414 | }
415 |
416 | methods[options.method]( )
417 |
--------------------------------------------------------------------------------
/COPYING:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015, AI2ASS contributors
2 |
3 | Permission to use, copy, modify, and/or distribute this software for any
4 | purpose with or without fee is hereby granted, provided that the above
5 | copyright notice and this permission notice appear in all copies.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## AI2ASS ##
2 | ![sweet screenshot][screenshit]
3 |
4 | This is a script to export† drawings in Adobe Illustrator as ASS
5 | vector objects. It was inspired by tophf's similar script for CorelDRAW.
6 |
7 | †The exporting process consists of sticking generated
8 | ASS into a window that has copyable text. This is still terrible design,
9 | but at least it's slightly better than it used to be.
10 |
11 | ### Features and Limitations
12 |
13 | ##### It only acts upon the currently focused document in Illustrator.
14 |
15 | ##### It should work with most versions of Illustrator.
16 | Has been used with CS5, CS6 and CC versions of Illustrator.
17 |
18 | ##### Can export the current layer or all layers at once.
19 |
20 | ##### Shapes are separated both by layer and the prefixes of the chosen output format (which may include color, stroke, opacity and clipping paths).
21 | Paths that share all these attributes can be merged into a single line.
22 | The following merge strategies are available:
23 |
24 | + __Disabled__: turns line merging off
25 | + __Safe__: merges lines in a way that doesn't disturb your scene graph order
26 | + __Ignore Blending Order__: merges all paths of a layer sharing a common prefix without respect to their order within the layer.
27 |
28 | ##### It supports RGB and Grayscale colorspaces
29 | CMYK support may be added at some point in the future if I ever find a
30 | conversion algorithm that works properly.
31 |
32 | ##### It can output shapes wrapped in {\p1}, \clip, \iclip, raw shape data, or complete dialogue lines.
33 | All output shape data uses coordinates with two decimal places of
34 | precision. Modern ASS renderers should be able to handle these properly.
35 | If you are worried about people running horribly old and terrible
36 | software, don't. If you're using this to do typesetting, odds are it'll
37 | be too slow to run on their setup anyway.
38 |
39 | ##### Exports clipping paths as \clips of their respective lines
40 |
41 | ##### There is basic transparency support
42 | AI2ASS correctly calculates the opacity for every path and exports it as
43 | `\alpha` override tag. However, output will only be correct when not
44 | using any of the blending modes unsupported in ASS (which is all of them
45 | except the *Normal* mode).
46 |
47 | ### Great, but how do I run it? ###
48 |
49 | Place [`AI2ASS.jsx`][raw] in your Illustrator
50 | scripts folder.
51 |
52 | On OS X, the scripts folder should be something like
53 | `/Applications/Adobe Illustrator CS6/Presets/en_US/Scripts`.
54 |
55 | On Windows, it'll be `C:\Program Files\Adobe\Adobe Illustrator CS6 (64
56 | Bit)\Presets\en_US\Scripts` , assuming you're running the 64-bit version
57 | (thanks, __ar).
58 |
59 | If you launch Illustrator, the script should now appear in the menu as
60 | `File > Scripts > AI2ASS`. Running this will pop up a persistent window
61 | with a button you can click to convert the active layer into an ASS
62 | drawing. You don't need to close this ever. It's neat.
63 |
64 | #### WARNING: SHIT MOVES SLOW WHEN YOU HAVE A LOT OF STUFF GOING ON ####
65 |
66 | But there's a cool progress bar so you can see that it's going slow.
67 |
68 | ### TODO ###
69 | - AINT NOTHIN THIS IS PERFECT
70 |
71 | #### License
72 |
73 | ISC. See COPYING for details.
74 |
75 | [screenshit]: https://raw.github.com/torque/AI2ASS/master/screenshot.png
76 | [raw]: https://raw.github.com/torque/AI2ASS/master/built/AI2ASS.jsx
77 |
--------------------------------------------------------------------------------
/applyLines.moon:
--------------------------------------------------------------------------------
1 | -- This script takes one line and turns it into many lines.
2 |
3 | -- Usage: select 1 line (or a bunch, it actually doesn't matter because it only
4 | -- uses the active line). Run the macro. It will copy all of the shape data from
5 | -- AI2ASS on the clipboard into Aegisub, creating as many lines as necessary.
6 | -- Note that due to me being a fucking idiot, it will just kind of paste
7 | -- whatever the hell you have on the clipboard into the current line. I don't
8 | -- care to fix this. I doubt anyone except me will ever use this. If you do and
9 | -- you don't like this behavior, pull requests welcome.
10 |
11 | require "clipboard"
12 |
13 | string.split = ( sep ) =>
14 | sep, fields = sep or ":", {}
15 | string.gsub @, "([^#{sep}]+)", (c) -> table.insert fields, c
16 | fields
17 |
18 | -- Creates lines on a glyph by glyph basis because libass eats shit on very long
19 | -- lines. This looks terrible on fades and ends up bloating the script a lot
20 | -- more, but it's easier than actually fixing libass myself. Oh, and all the
21 | -- colors are hardcoded because I don't give half a shit. Fix it yourself if you
22 | -- want to use it.
23 | applyInnerShadow = ( sub, originalLine, lineTable ) ->
24 | len = #lineTable
25 | for x = 2, len-2, 2
26 | error "DONGS" if aegisub.progress.is_cancelled!
27 | originalLine.text = [[{\pos(2,2)\c&H000000&\blur2}]] .. lineTable[x]
28 | originalLine.layer = 2
29 | sub.insert originalLine.n, originalLine
30 | originalLine.text = [[{\pos(0,0)\c&H0000FF&}]] .. lineTable[x-1]
31 | originalLine.layer = 1
32 | sub.insert originalLine.n, originalLine
33 | originalLine.text = [[{\3c&HFFFFFF&\bord8}]] .. originalLine.text
34 | originalLine.layer = 0
35 | sub.insert originalLine.n, originalLine
36 |
37 | originalLine.text = [[{\pos(2,2)\c&H000000&\blur2}]] .. lineTable[len]
38 | originalLine.layer = 2
39 | sub.insert originalLine.n, originalLine
40 | originalLine.text = [[{\pos(0,0)\c&H0000FF&}]] .. lineTable[len-1]
41 | originalLine.layer = 1
42 | sub.insert originalLine.n, originalLine
43 | originalLine.text = [[{\3c&HFFFFFF&\bord8}]] .. originalLine.text
44 | originalLine.layer = 0
45 | sub[originalLine.n-1] = originalLine
46 |
47 | createInnerShadow = ( sub, sel, act ) ->
48 | originalLine = sub[act]
49 | originalLine.n = act + 1
50 | incomingLines = clipboard.get!
51 | lineTable = incomingLines\split "\r\n"
52 | if lineTable[1]\match "{innerShadow}"
53 | table.remove lineTable, 1
54 | if #lineTable % 2 == 0
55 | applyInnerShadow sub, originalLine, lineTable
56 | else
57 | aegisub.log 0, "This shit is malformed and everything you do is wrong."
58 | elseif lineTable[1]\match "{allLayers}"
59 | table.remove lineTable, 1
60 | applyAllLayers sub, originalLine, lineTable
61 | else
62 | originalLine.text = lineTable
63 | sub[act] = originalLine
64 |
65 | aegisub.register_macro "Apply AI Lines", "Handles paste data from AI2ASS", createInnerShadow
66 |
--------------------------------------------------------------------------------
/build.ps1:
--------------------------------------------------------------------------------
1 | gc "AI2ASS.coffee" | Out-File AI2ASScomb.coffee -e UTF8
2 | gc "AI2ASS UI.coffee" | Out-File AI2ASScomb.coffee -e UTF8 -a
3 | coffee -bc AI2ASScomb.coffee
4 | mkdir built -f
5 | mv AI2ASScomb.js built/AI2ASS.jsx -force
6 | rm AI2ASScomb.coffee
7 |
--------------------------------------------------------------------------------
/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | cat AI2ASS.coffee "AI2ASS UI.coffee" > AI2ASScomb.coffee
4 | coffee -bc AI2ASScomb.coffee
5 | mv AI2ASScomb.js built/AI2ASS.jsx
6 | rm AI2ASScomb.coffee
7 |
--------------------------------------------------------------------------------
/built/AI2ASS.jsx:
--------------------------------------------------------------------------------
1 | #target illustrator;
2 | #targetengine main;
3 | var ai2assBackend, backendScript, bt, dlg, dlgRes, exportMethods, k, objToString, outputFormats, pathCombiningStrategies, radioString, v, win;
4 |
5 | ai2assBackend = function(options) {
6 | var ASS_createDrawingFromPoints, black, checkLinear, countPathItems, doc, drawing, handleGray, handleRGB, manageColor, manageOpacity, methods, org, pWin, run, zeroPad;
7 | app.userInteractionLevel = UserInteractionLevel.DISPLAYALERTS;
8 | pWin = new Window("palette");
9 | pWin.text = "Progress Occurs";
10 | pWin.pBar = pWin.add("progressbar", void 0, 0, 250);
11 | pWin.pBar.preferredSize = [250, 10];
12 | doc = app.activeDocument;
13 | org = doc.rulerOrigin;
14 | black = new RGBColor();
15 | countPathItems = function(obj) {
16 | var count, recurse;
17 | recurse = function(obj) {
18 | var i, l, layer, len, len1, len2, m, pageItem, path, ref, ref1, ref2, results, results1, results2;
19 | if (!obj.hidden) {
20 | switch (obj.typename) {
21 | case "Document":
22 | ref = obj.layers;
23 | results = [];
24 | for (i = 0, len = ref.length; i < len; i++) {
25 | layer = ref[i];
26 | results.push(recurse(layer));
27 | }
28 | return results;
29 | break;
30 | case "Layer":
31 | case "GroupItem":
32 | ref1 = obj.pageItems;
33 | results1 = [];
34 | for (l = 0, len1 = ref1.length; l < len1; l++) {
35 | pageItem = ref1[l];
36 | results1.push(recurse(pageItem));
37 | }
38 | return results1;
39 | break;
40 | case "CompoundPathItem":
41 | ref2 = obj.pathItems;
42 | results2 = [];
43 | for (m = 0, len2 = ref2.length; m < len2; m++) {
44 | path = ref2[m];
45 | results2.push(recurse(path));
46 | }
47 | return results2;
48 | break;
49 | case "PathItem":
50 | return count += 1;
51 | }
52 | }
53 | };
54 | count = 0;
55 | recurse(obj);
56 | return count;
57 | };
58 | run = function(root, includeEmptyLayers) {
59 | var output;
60 | if (root == null) {
61 | root = doc;
62 | }
63 | output = {
64 | combineStrategy: "safe",
65 | layers: [],
66 | pathCnt: null,
67 | processedPathCnt: 0,
68 | tempLayer: null,
69 | makeTempLayer: function(name) {
70 | if (name == null) {
71 | name = "AI2ASS_tmp";
72 | }
73 | this.tempLayer = doc.layers.add();
74 | this.tempLayer.name = name;
75 | return this.tempLayer.zOrder(ZOrderMethod.SENDTOBACK);
76 | },
77 | makeClip: function(clippingPath) {
78 | var clip;
79 | clip = {
80 | tempGroup: null,
81 | isVisible: false,
82 | output: this,
83 | add: function(clippingPath) {
84 | var copy, prevSelection;
85 | if (this.output.tempLayer == null) {
86 | this.output.makeTempLayer();
87 | }
88 | if (!this.tempGroup) {
89 | this.tempGroup = this.output.tempLayer.groupItems.add();
90 | }
91 | copy = clippingPath.duplicate(this.tempGroup, ElementPlacement.PLACEATBEGINNING);
92 | copy.filled = true;
93 | copy.stroked = false;
94 | copy.clipping = false;
95 | copy.fillColor = black;
96 | if (this.tempGroup.pageItems.length > 1) {
97 | prevSelection = doc.selection;
98 | doc.selection = [this.tempGroup];
99 | app.executeMenuCommand("Live Pathfinder Intersect");
100 | app.executeMenuCommand("expandStyle");
101 | this.tempGroup = doc.selection[0];
102 | if (this.tempGroup.pageItems.length === 1) {
103 | this.isVisible = true;
104 | } else {
105 | this.isVisible = false;
106 | this.tempGroup.pageItems.removeAll();
107 | }
108 | return doc.selection = prevSelection;
109 | } else {
110 | return this.isVisible = true;
111 | }
112 | },
113 | copy: function() {
114 | return makeClip(this.tempGroup.pageItems[0]);
115 | },
116 | get: function() {
117 | return this.tempGroup.pageItems[0];
118 | },
119 | getASS: function() {
120 | var drawing;
121 | drawing = ASS_createDrawingFromPoints(this.tempGroup.pageItems[0].pathPoints);
122 | return "\\clip(" + (drawing.join(' ')) + ")";
123 | }
124 | };
125 | clip.add(clippingPath);
126 | return clip;
127 | },
128 | makeLayer: function(emptyPrefix) {
129 | var layer;
130 | layer = {
131 | groups: [],
132 | currGroupIdx: -1,
133 | currGroup: null,
134 | emptyPrefix: null,
135 | makeMergeGroup: function() {
136 | var group;
137 | group = {
138 | dirtyRects: [],
139 | lines: {},
140 | layer: this,
141 | addPath: function(path, prefix) {
142 | var drawing;
143 | if (!this.isZeroArea(path.visibleBounds)) {
144 | this.dirtyRects.push(path.visibleBounds);
145 | drawing = ASS_createDrawingFromPoints(path.pathPoints);
146 | if (this.lines[prefix] != null) {
147 | return Array.prototype.push.apply(this.lines[prefix], drawing);
148 | } else {
149 | return this.lines[prefix] = drawing;
150 | }
151 | }
152 | },
153 | isZeroArea: function(bounds) {
154 | return bounds[2] - bounds[0] === 0 && bounds[3] - bounds[1] === 0;
155 | },
156 | isMergeable: function(path) {
157 | var bounds, i, len, rect, ref;
158 | if (path.parent.typename === "CompoundPathItem") {
159 | return true;
160 | }
161 | switch (this.layer.combineStrategy) {
162 | case "off":
163 | return false;
164 | case "any":
165 | return true;
166 | case "safe":
167 | bounds = path.visibleBounds;
168 | if (this.isZeroArea(bounds)) {
169 | return true;
170 | }
171 | ref = this.dirtyRects;
172 | for (i = 0, len = ref.length; i < len; i++) {
173 | rect = ref[i];
174 | if (bounds[2] > rect[0] && bounds[0] < rect[2] && bounds[3] < rect[1] && bounds[1] > rect[3]) {
175 | return false;
176 | }
177 | }
178 | return true;
179 | }
180 | }
181 | };
182 | return group;
183 | },
184 | addGroup: function() {
185 | this.currGroupIdx += 1;
186 | this.currGroup = this.makeMergeGroup();
187 | return this.groups[this.currGroupIdx] = this.currGroup;
188 | },
189 | addPath: function(path, prefix) {
190 | if (!this.currGroup.isMergeable(path)) {
191 | this.addGroup();
192 | }
193 | return this.currGroup.addPath(path, prefix);
194 | }
195 | };
196 | layer.emptyPrefix = emptyPrefix;
197 | layer.combineStrategy = this.combineStrategy;
198 | layer.addGroup();
199 | return layer;
200 | },
201 | process: function(obj, clip, opacity) {
202 | var clipPath, i, l, layer, m, n, pI, path, ref, ref1, ref2, ref3, results, results1, results2, results3, subPageItem;
203 | if (opacity == null) {
204 | opacity = 100;
205 | }
206 | if (this.pathCnt == null) {
207 | this.pathCnt = countPathItems(obj);
208 | }
209 | if (!obj.hidden && ((clip == null) || clip.isVisible)) {
210 | opacity = obj.opacity != null ? opacity * obj.opacity / 100 : 100;
211 | switch (obj.typename) {
212 | case "Document":
213 | ref = obj.layers;
214 | results = [];
215 | for (i = ref.length - 1; i >= 0; i += -1) {
216 | layer = ref[i];
217 | results.push(this.process(layer));
218 | }
219 | return results;
220 | break;
221 | case "Layer":
222 | if (obj.pageItems.length === 0) {
223 | return this.layers[obj.zOrderPosition] = this.makeLayer(this.emptyPrefix(obj.zOrderPosition, obj.name));
224 | } else {
225 | ref1 = obj.pageItems;
226 | results1 = [];
227 | for (l = ref1.length - 1; l >= 0; l += -1) {
228 | subPageItem = ref1[l];
229 | results1.push(this.process(subPageItem, null, opacity));
230 | }
231 | return results1;
232 | }
233 | break;
234 | case "CompoundPathItem":
235 | ref2 = obj.pathItems;
236 | results2 = [];
237 | for (m = ref2.length - 1; m >= 0; m += -1) {
238 | path = ref2[m];
239 | results2.push(this.process(path, clip, opacity));
240 | }
241 | return results2;
242 | break;
243 | case "GroupItem":
244 | if (obj.clipped) {
245 | clipPath = ((function() {
246 | var len, n, ref3, results3;
247 | ref3 = obj.pageItems;
248 | results3 = [];
249 | for (n = 0, len = ref3.length; n < len; n++) {
250 | pI = ref3[n];
251 | if (pI.clipping) {
252 | results3.push(pI);
253 | }
254 | }
255 | return results3;
256 | })())[0];
257 | if (clip != null) {
258 | clip = clip.copy();
259 | clip.add(clipPath);
260 | } else {
261 | clip = this.makeClip(clipPath);
262 | }
263 | this.processedPathCnt += 1;
264 | }
265 | ref3 = obj.pageItems;
266 | results3 = [];
267 | for (n = ref3.length - 1; n >= 0; n += -1) {
268 | subPageItem = ref3[n];
269 | if (!subPageItem.clipping) {
270 | results3.push(this.process(subPageItem, clip, opacity));
271 | }
272 | }
273 | return results3;
274 | break;
275 | case "PathItem":
276 | if (this.processedPathCnt % 10 === 0) {
277 | pWin.pBar.value = Math.ceil(this.processedPathCnt * 250 / this.pathCnt);
278 | pWin.update();
279 | }
280 | if (!(obj.guides || !(obj.stroked || obj.filled || obj.clipping) || !obj.layer.visible)) {
281 | this.appendPath(obj, clip, opacity);
282 | }
283 | return this.processedPathCnt += 1;
284 | }
285 | }
286 | },
287 | appendPath: function(path, clipObj, opacity) {
288 | var alpha, clip, fill, layer, layerName, layerNum, prefix, stroke;
289 | stroke = manageColor(path, "strokeColor", 3);
290 | fill = manageColor(path, "fillColor", 1);
291 | layerName = path.layer.name;
292 | layerNum = path.layer.zOrderPosition;
293 | alpha = manageOpacity(opacity);
294 | clip = clipObj != null ? clipObj.getASS() : "";
295 | prefix = this.prefix(stroke, fill, clip, alpha, layerNum, layerName);
296 | layer = this.layers[layerNum];
297 | if (layer == null) {
298 | layer = this.makeLayer();
299 | this.layers[layerNum] = layer;
300 | }
301 | return layer.addPath(path, prefix);
302 | },
303 | prefix: function(stroke, fill, clip, alpha) {
304 | return "{\\an7\\pos(0,0)" + stroke + fill + alpha + clip + "\\p1}";
305 | },
306 | emptyPrefix: function() {
307 | return "";
308 | },
309 | suffix: function() {
310 | return "{\\p0}";
311 | },
312 | get: function(includeEmptyLayers) {
313 | var drawing, fragments, i, l, layer, len, len1, mergeGroup, prefix, ref, ref1, ref2, suffix;
314 | fragments = [];
315 | suffix = this.suffix();
316 | ref = this.layers;
317 | for (i = 0, len = ref.length; i < len; i++) {
318 | layer = ref[i];
319 | if (!(layer != null)) {
320 | continue;
321 | }
322 | if (includeEmptyLayers && (layer.emptyPrefix != null)) {
323 | fragments.push(layer.emptyPrefix);
324 | fragments.push("\n");
325 | }
326 | ref1 = layer.groups;
327 | for (l = 0, len1 = ref1.length; l < len1; l++) {
328 | mergeGroup = ref1[l];
329 | ref2 = mergeGroup.lines;
330 | for (prefix in ref2) {
331 | drawing = ref2[prefix];
332 | fragments.push(prefix);
333 | fragments.push(drawing.join(" "));
334 | fragments.push(suffix);
335 | fragments.push("\n");
336 | }
337 | }
338 | }
339 | fragments.pop();
340 | return fragments.join("");
341 | }
342 | };
343 | if (options.combineStrategy != null) {
344 | output.combineStrategy = options.combineStrategy;
345 | }
346 | switch (options.wrapper) {
347 | case "clip":
348 | output.prefix = function() {
349 | return "\\clip(";
350 | };
351 | output.suffix = function() {
352 | return ")";
353 | };
354 | break;
355 | case "iclip":
356 | output.prefix = function() {
357 | return "\\iclip(";
358 | };
359 | output.suffix = function() {
360 | return ")";
361 | };
362 | break;
363 | case "bare":
364 | output.prefix = function() {
365 | return "";
366 | };
367 | output.suffix = function() {
368 | return "";
369 | };
370 | break;
371 | case "line":
372 | output.prefix = function(stroke, fill, clip, alpha, layerNum, layerName) {
373 | return "Dialogue: " + layerNum + ",0:00:00.00,0:00:00.00,AI," + layerName + ",0,0,0,,{\\an7\\pos(0,0)" + stroke + fill + alpha + clip + "\\p1}";
374 | };
375 | output.suffix = function() {
376 | return "";
377 | };
378 | output.emptyPrefix = function(layerNum, layerName) {
379 | return "Dialogue: " + layerNum + ",0:00:00.00,0:00:00.00,AI," + layerName + ",0,0,0,,";
380 | };
381 | }
382 | if (doc.documentColorSpace === DocumentColorSpace.CMYK) {
383 | alert("Your colorspace needs to be RGB if you want colors.");
384 | }
385 | pWin.show();
386 | output.process(root);
387 | if (output.tempLayer != null) {
388 | output.tempLayer.remove();
389 | }
390 | pWin.close();
391 | return output.get(includeEmptyLayers);
392 | };
393 | drawing = {
394 | commands: [],
395 | "new": function() {
396 | return this.commands = [];
397 | },
398 | get: function() {
399 | return this.commands;
400 | },
401 | CmdTypes: {
402 | None: -1,
403 | Move: 0,
404 | Linear: 1,
405 | Cubic: 2
406 | },
407 | prevCmdType: -1,
408 | addMove: function(point) {
409 | this.commands.push("m");
410 | this.addCoords(point.anchor);
411 | return this.prevCmdType = this.CmdTypes.Move;
412 | },
413 | addLinear: function(point) {
414 | if (this.prevCmdType !== this.CmdTypes.Linear) {
415 | this.commands.push("l");
416 | this.prevCmdType = this.CmdTypes.Linear;
417 | }
418 | this.commands.push;
419 | return this.addCoords(point.anchor);
420 | },
421 | addCubic: function(currPoint, prevPoint) {
422 | if (this.prevCmdType !== this.CmdTypes.Cubic) {
423 | this.commands.push("b");
424 | this.prevCmdType = this.CmdTypes.Cubic;
425 | }
426 | this.addCoords(prevPoint.rightDirection);
427 | this.addCoords(currPoint.leftDirection);
428 | return this.addCoords(currPoint.anchor);
429 | },
430 | addCoords: function(coordArr) {
431 | this.commands.push(Math.round((coordArr[0] + org[0]) * 100) / 100);
432 | return this.commands.push(Math.round((doc.height - (org[1] + coordArr[1])) * 100) / 100);
433 | }
434 | };
435 | checkLinear = function(currPoint, prevPoint) {
436 | var p1, p2;
437 | p1 = prevPoint.anchor[0] === prevPoint.rightDirection[0] && prevPoint.anchor[1] === prevPoint.rightDirection[1];
438 | p2 = currPoint.anchor[0] === currPoint.leftDirection[0] && currPoint.anchor[1] === currPoint.leftDirection[1];
439 | return p1 && p2;
440 | };
441 | zeroPad = function(num) {
442 | var hexStr;
443 | hexStr = num.toString(16).toUpperCase();
444 | if (num < 16) {
445 | return "0" + hexStr;
446 | } else {
447 | return hexStr;
448 | }
449 | };
450 | handleGray = function(theColor) {
451 | var pct;
452 | pct = theColor.gray;
453 | pct = Math.round((100 - pct) * 255 / 100);
454 | return "&H" + (zeroPad(pct)) + (zeroPad(pct)) + (zeroPad(pct)) + "&";
455 | };
456 | handleRGB = function(theColor) {
457 | var b, g, r;
458 | r = Math.round(theColor.red);
459 | g = Math.round(theColor.green);
460 | b = Math.round(theColor.blue);
461 | return "&H" + (zeroPad(b)) + (zeroPad(g)) + (zeroPad(r)) + "&";
462 | };
463 | manageColor = function(currPath, field, ASSField) {
464 | var fmt;
465 | fmt = "";
466 | switch (currPath[field].typename) {
467 | case "RGBColor":
468 | fmt = handleRGB(currPath[field]);
469 | break;
470 | case "GrayColor":
471 | fmt = handleGray(currPath[field]);
472 | break;
473 | case "NoColor":
474 | switch (field) {
475 | case "fillColor":
476 | return "\\" + ASSField + "a&HFF&";
477 | case "strokeColor":
478 | return "";
479 | }
480 | break;
481 | default:
482 | return "";
483 | }
484 | return "\\" + ASSField + "c" + fmt;
485 | };
486 | manageOpacity = function(opacity) {
487 | if (opacity >= 100) {
488 | return "";
489 | }
490 | return "\\alpha&H" + (zeroPad(255 - Math.round(opacity) / 100 * 255)) + "&";
491 | };
492 | ASS_createDrawingFromPoints = function(pathPoints) {
493 | var currPoint, i, j, prevPoint, ref;
494 | drawing["new"]();
495 | if (pathPoints.length > 0) {
496 | drawing.addMove(pathPoints[0]);
497 | for (j = i = 1, ref = pathPoints.length; i < ref; j = i += 1) {
498 | currPoint = pathPoints[j];
499 | prevPoint = pathPoints[j - 1];
500 | if (checkLinear(currPoint, prevPoint)) {
501 | drawing.addLinear(currPoint);
502 | } else {
503 | drawing.addCubic(currPoint, prevPoint);
504 | }
505 | }
506 | prevPoint = pathPoints[pathPoints.length - 1];
507 | currPoint = pathPoints[0];
508 | if (checkLinear(currPoint, prevPoint)) {
509 | drawing.addLinear(currPoint);
510 | } else {
511 | drawing.addCubic(currPoint, prevPoint);
512 | }
513 | return drawing.get();
514 | }
515 | };
516 | methods = {
517 | collectActiveLayer: function() {
518 | var currLayer;
519 | currLayer = doc.activeLayer;
520 | if (!currLayer.visible) {
521 | return "Not doing anything to that invisible layer.";
522 | }
523 | return run(currLayer);
524 | },
525 | collectAllLayers: function() {
526 | return run();
527 | },
528 | collectAllLayersIncludeEmpty: function() {
529 | return run(doc, true);
530 | }
531 | };
532 | return methods[options.method]();
533 | };
534 |
535 | dlgRes = "Group { orientation:'column', alignChildren: ['fill', 'fill'], output: Panel { orientation:'column', text: 'ASS Output', edit: EditText {text: 'have ass, will typeset', properties: {multiline: true}, alignment: ['fill', 'fill'], preferredSize: [-1, 100] } }, outputFormat: Panel { orientation:'column', text: 'Output Format', clip: Group {orientation: 'row', alignChildren: ['fill', 'fill'], spacing: 5, noclip: RadioButton {text: 'Drawing', value: true}, clip: RadioButton {text: '\\\\clip'}, iclip: RadioButton {text: '\\\\iclip'}, bare: RadioButton {text: 'Bare'}, line: RadioButton {text: 'Line'} }, }, settings: Panel {orientation: 'column', alignChildren: ['left','fill'], text: 'Settings', collectionTarget: DropDownList {title: 'Collection Target:'}, pathCombining: DropDownList {title: 'Path Combining:'} }, export: Button {text: 'Export'} }";
536 |
537 | win = new Window("palette", "Export ASS", void 0, {});
538 |
539 | dlg = win.add(dlgRes);
540 |
541 | outputFormats = {
542 | "Drawing:": "noclip",
543 | "\\clip": "clip",
544 | "\\iclip": "iclip",
545 | "Bare": "bare",
546 | "Line": "line"
547 | };
548 |
549 | exportMethods = {
550 | "Active Layer": "collectActiveLayer",
551 | "Non-Empty Layers": "collectAllLayers",
552 | "All Layers": "collectAllLayersIncludeEmpty"
553 | };
554 |
555 | for (k in exportMethods) {
556 | v = exportMethods[k];
557 | dlg.settings.collectionTarget.add("item", k);
558 | }
559 |
560 | dlg.settings.collectionTarget.selection = 0;
561 |
562 | pathCombiningStrategies = {
563 | "Disabled": "off",
564 | "Safe (Maintain Order)": "safe",
565 | "Ignore Blending Order": "any"
566 | };
567 |
568 | for (k in pathCombiningStrategies) {
569 | v = pathCombiningStrategies[k];
570 | dlg.settings.pathCombining.add("item", k);
571 | }
572 |
573 | dlg.settings.pathCombining.selection = 1;
574 |
575 | bt = new BridgeTalk;
576 |
577 | bt.target = "illustrator";
578 |
579 | backendScript = ai2assBackend.toString();
580 |
581 | radioString = function(radioGroup) {
582 | var child, i, len, ref;
583 | ref = radioGroup.children;
584 | for (i = 0, len = ref.length; i < len; i++) {
585 | child = ref[i];
586 | if (child.value) {
587 | return outputFormats[child.text];
588 | }
589 | }
590 | };
591 |
592 | objToString = function(obj) {
593 | var fragments;
594 | fragments = ((function() {
595 | var results;
596 | results = [];
597 | for (k in obj) {
598 | v = obj[k];
599 | results.push(k + ": \"" + v + "\"");
600 | }
601 | return results;
602 | })()).join(", ");
603 | return "{" + fragments + "}";
604 | };
605 |
606 | dlg["export"].onClick = function() {
607 | var options;
608 | dlg.output.edit.active = false;
609 | options = objToString({
610 | method: exportMethods[dlg.settings.collectionTarget.selection.text],
611 | wrapper: radioString(dlg.outputFormat.clip),
612 | combineStrategy: pathCombiningStrategies[dlg.settings.pathCombining.selection.text]
613 | });
614 | bt.body = "(" + backendScript + ")(" + options + ");";
615 | bt.onResult = function(result) {
616 | dlg.output.edit.text = result.body.replace(/\\\\/g, "\\").replace(/\\n/g, "\n");
617 | return dlg.output.edit.active = true;
618 | };
619 | bt.onError = function(err) {
620 | return alert(err.body + " (" + a.headers["Error-Code"] + ")");
621 | };
622 | return bt.send();
623 | };
624 |
625 | win.show();
626 |
--------------------------------------------------------------------------------
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TypesettingTools/AI2ASS/b5bd9706cefce87ae70779f437ca70a25b2e7e6b/screenshot.png
--------------------------------------------------------------------------------