Skip to content

Instantly share code, notes, and snippets.

@sttk3
Created February 9, 2022 13:51
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sttk3/1727713c6c182c40df172112279fd2ba to your computer and use it in GitHub Desktop.
Save sttk3/1727713c6c182c40df172112279fd2ba to your computer and use it in GitHub Desktop.
/**
* @file trim strokes
* https://community.adobe.com/t5/illustrator-discussions/creating-a-sliced-path-dashed-dotted-path/m-p/12736845
* @version 0.1.0
* @author sttk3.com
* @copyright © 2022 sttk3.com
*/
//@target 'illustrator'
//@targetengine 'com.sttk3.trimStroke'
$.localize = true ;
(function() {
if(app.documents.length <= 0) {return ;}
var doc = app.documents[0] ;
var sel = doc.selection ;
if(sel.length < 2) {return ;}
// reject when selected item is not a path, not a dashed stroke, or has a fill
var patternPathItem = /^PathItem$/ ;
var targetItems = filterItems(sel, function(aItem) {
return (
patternPathItem.test(aItem.constructor.name)
&& !aItem.filled
) ;
}) ;
if(targetItems.length != 2) {
var message = {
en: 'There was no target. \nPlease select two strokes with no fill and try again.',
ja: '対象がありませんでした。\n塗りのない線を2つ選択してから実行してください。',
}
alert(message) ;
return ;
}
// generate a unique color
var tempSpot = doc.spots.add() ;
var tempColor = new SpotColor() ;
tempColor.spot = tempSpot ;
tempColor.tint = 100 ;
// subtract target strokes
var newSelection = [] ;
try {
var newItem1 = subtractStroke(doc, targetItems[0], targetItems[1], tempColor) ;
if(newItem1) {newSelection.push(newItem1) ;}
var newItem2 = expandDashedStroke(doc, targetItems[0], tempColor) ;
if(newItem2) {newSelection.push(newItem2) ;}
newItem2.move(newItem1, ElementPlacement.PLACEBEFORE) ;
} catch(e) {
alert(e) ;
return ;
} finally {
tempSpot.remove() ;
}
if(newSelection.length > 0) {
doc.selection = newSelection ;
app.executeMenuCommand('group') ;
}
})() ;
/**
* something like Array.filter
* @param {Array} targetItems Array or collection of targets, anything with length and index
* @param {Function} callback test function
* @return {Array}
*/
function filterItems(targetItems, callback) {
var res = [] ;
for(var i = 0, len = targetItems.length ; i < len ; i++) {
if(i in targetItems) {
var val = targetItems[i] ;
if(callback.call(targetItems, val, i)) {res.push(val) ;}
}
}
return res ;
}
/**
* subtract mainItem from subItem
* @param {Document} doc target document
* @param {PathItem} mainItem dashed stroke to deduct
* @param {PathItem} subItem stroke to be subtracted
* @param {SpotColor} tempColor unique color. a marker to identify what is unnecessary
* @return {GroupItem}
*/
function subtractStroke(doc, mainItem, subItem, tempColor) {
var tempSpot = tempColor.spot ;
// object to fill dash
var dashPath = mainItem.duplicate(mainItem, ElementPlacement.PLACEBEFORE) ;
dashPath.strokeColor = tempColor ;
dashPath.strokeCap = StrokeCap.BUTTENDCAP ; // make the dash length unchangeable
var backupPath ;
var oldUserInteractionLevel = app.userInteractionLevel ;
try {
app.userInteractionLevel = UserInteractionLevel.DONTDISPLAYALERTS ;
backupPath = subItem.duplicate(subItem, ElementPlacement.PLACEAFTER) ;
doc.selection = [dashPath] ;
app.executeMenuCommand('OffsetPath v22') ; // Outline Stroke
subItem.selected = true ;
app.executeMenuCommand('Make Planet X') ; // Live Paint Make
app.executeMenuCommand('Expand Planet X') ; // Live Paint Expand
app.redraw() ;
// reject unexpected structures
var tempGroups = doc.selection[0].groupItems ;
if(tempGroups.length <= 0) {
doc.selection[0].remove() ;
var message = {
en: 'Paths with multiple strokes are not supported.',
ja: '複数の線を持つパスには対応していません。'
}
// mark it as a special error
var newError = new Error(message) ;
newError.number = -128 ;
throw newError ;
}
// remove dashPath
var itemColor ;
for(var i = tempGroups.length - 1 ; i >= 0 ; i--) {
itemColor = tempGroups[i].pathItems[0].fillColor ;
if((itemColor.constructor.name == 'SpotColor') && (itemColor.spot == tempSpot)) {
tempGroups[i].remove() ;
break ;
}
}
backupPath.remove() ;
res = doc.selection[0] ;
} catch(e) {
alert(e) ;
if(e.number == -128) {res = backupPath ;}
return res ;
} finally {
app.userInteractionLevel = oldUserInteractionLevel ;
}
return res ;
}
/**
* expand a dashed stroke
* @param {Document} doc target document
* @param {PathItem} targetItem target dashed stroke
* @param {SpotColor} tempColor unique color. a marker to identify what is unnecessary
* @return {GroupItem}
*/
function expandDashedStroke(doc, targetItem, tempColor) {
var originalPath = targetItem ;
var tempSpot = tempColor.spot ;
var pathfinderGroup = originalPath.layer.groupItems.add() ;
pathfinderGroup.move(originalPath, ElementPlacement.PLACEBEFORE) ;
var maskWidth = originalPath.strokeWidth + 1 ;
// object to fill dash
var dashPath = originalPath.duplicate(pathfinderGroup, ElementPlacement.PLACEATEND) ;
dashPath.strokeWidth = maskWidth ;
dashPath.strokeCap = StrokeCap.BUTTENDCAP ; // make the dash length unchangeable
// object to fill gap
var gapPath = originalPath.duplicate(pathfinderGroup, ElementPlacement.PLACEATEND) ;
gapPath.strokeDashes = [] ;
gapPath.strokeColor = tempColor ;
var backupPath ;
var oldUserInteractionLevel = app.userInteractionLevel ;
try {
app.userInteractionLevel = UserInteractionLevel.DONTDISPLAYALERTS ;
backupPath = originalPath.duplicate(originalPath, ElementPlacement.PLACEAFTER) ;
// leave fill only at gaps
doc.selection = [pathfinderGroup] ;
app.executeMenuCommand('OffsetPath v22') ; // Outline Stroke
app.executeMenuCommand('Live Pathfinder Subtract') ;
app.executeMenuCommand('expandStyle') ; // Expand Appearance
app.executeMenuCommand('compoundPath') ; // Compound Path Make
// remove gaps of originalPath by live paint
originalPath.strokeDashes = [] ;
originalPath.selected = true ;
app.executeMenuCommand('Make Planet X') ; // Live Paint Make
app.executeMenuCommand('Expand Planet X') ; // Live Paint Expand
app.redraw() ;
// reject unexpected structures
var tempGroups = doc.selection[0].groupItems ;
if(tempGroups.length <= 0) {
doc.selection[0].remove() ;
var message = {
en: 'Paths with multiple strokes are not supported.',
ja: '複数の線を持つパスには対応していません。'
}
// mark it as a special error
var newError = new Error(message) ;
newError.number = -128 ;
throw newError ;
}
// remove gapPath
var itemColor ;
for(var i = tempGroups.length - 1 ; i >= 0 ; i--) {
itemColor = tempGroups[i].pathItems[0].fillColor ;
if((itemColor.constructor.name == 'SpotColor') && (itemColor.spot == tempSpot)) {
tempGroups[i].remove() ;
break ;
}
}
backupPath.remove() ;
res = doc.selection[0] ;
} catch(e) {
alert(e) ;
if(e.number == -128) {res = backupPath ;}
return res ;
} finally {
app.userInteractionLevel = oldUserInteractionLevel ;
}
return res ;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment