mirror of
https://github.com/sbrow/ps.git
synced 2025-12-29 18:47:38 -05:00
Updates, Improvements, and Fixes
* Moved scripts / runner to separate package
allows future replacement with PS Plugin.
* Fixed issues with Refresh and removed "layer" function.
* Added github documentation via godocdown.
* Reduced number of calls to Panic.
* Updated Tests
* Updated documentation.
* Fixed warnings.
* .gitignore now ignores .test and .out files.
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,2 +1,4 @@
|
|||||||
|
|
||||||
data/
|
data/
|
||||||
|
*.test
|
||||||
|
*.out
|
||||||
|
|||||||
9
.godocdown.template
Normal file
9
.godocdown.template
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# {{ .Name }}
|
||||||
|
[](https://godoc.org/github.com/sbrow/{{ .Name }}) [](https://travis-ci.org/sbrow/{{ .Name }}) [](https://coveralls.io/github/sbrow/{{ .Name }}?branch=master) [](https://goreportcard.com/report/github.com/sbrow/{{ .Name }})
|
||||||
|
|
||||||
|
{{ .EmitSynopsis }}
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
```bash
|
||||||
|
$ go get -u {{ .ImportPath }}
|
||||||
|
```
|
||||||
13
README.md
13
README.md
@@ -1,2 +1,13 @@
|
|||||||
# ps
|
# ps
|
||||||
Interface from Golang to Adobe Photoshop (CS5)
|
[](https://godoc.org/github.com/sbrow/ps) [](https://travis-ci.org/sbrow/ps) [](https://coveralls.io/github/sbrow/ps?branch=master) [](https://goreportcard.com/report/github.com/sbrow/ps)
|
||||||
|
|
||||||
|
Package ps is a rudimentary API between Adobe Photoshop CS5 and Golang. The
|
||||||
|
interaction between the two is implemented using Javascript/VBScript.
|
||||||
|
|
||||||
|
Currently only supports Photoshop CS5 Windows x86_64. go:generate godocdown
|
||||||
|
-output=README.md
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
```bash
|
||||||
|
$ go get -u github.com/sbrow/ps
|
||||||
|
```
|
||||||
|
|||||||
38
Variables.go
38
Variables.go
@@ -1,7 +1,5 @@
|
|||||||
package ps
|
package ps
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
// ModeEnum determines how aggressively the package will attempt to sync with Photoshop.
|
// ModeEnum determines how aggressively the package will attempt to sync with Photoshop.
|
||||||
// Loading Photoshop files from scratch takes a long time, so the package saves
|
// Loading Photoshop files from scratch takes a long time, so the package saves
|
||||||
// the state of the document in a JSON file in the /data folder whenever you call
|
// the state of the document in a JSON file in the /data folder whenever you call
|
||||||
@@ -11,36 +9,24 @@ type ModeEnum int
|
|||||||
// Mode holds the current mode.
|
// Mode holds the current mode.
|
||||||
var Mode ModeEnum
|
var Mode ModeEnum
|
||||||
|
|
||||||
const (
|
// Normal Mode only verifies layers as they are operated on. The first time a
|
||||||
// Normal Mode only verifies layers as they are operated on. The first time a
|
// layer's properties would be checked, it first overwrites the data from the
|
||||||
// layer's properties would be checked, it first overwrites the data from the
|
// Dump with data pulled directly from Photoshop. This allows you to quickly
|
||||||
// Dump with data pulled directly from Photoshop. This allows you to quickly
|
// load documents in their current form.
|
||||||
// load documents in their current form.
|
const Normal ModeEnum = 0
|
||||||
Normal ModeEnum = iota
|
|
||||||
|
|
||||||
// Safe Mode always loads the document from scratch, ignoring any dumped data.
|
// Safe Mode always loads the document from scratch, ignoring any dumped data.
|
||||||
// (Very Slow). If a function panics due to outdated data, often times re-running
|
// (Very Slow). If a function panics due to outdated data, often times re-running
|
||||||
// the function in safe mode is enough to remediate it.
|
// the function in safe mode is enough to remediate it.
|
||||||
Safe
|
const Safe ModeEnum = 1
|
||||||
|
|
||||||
// Fast mode skips all verification. Use Fast mode only when certain that the
|
// Fast mode skips all verification. Use Fast mode only when certain that the
|
||||||
// .psd file hasn't changed since the last time Document.Dump() was called.
|
// .psd file hasn't changed since the last time Document.Dump() was called.
|
||||||
Fast
|
const Fast ModeEnum = 2
|
||||||
)
|
|
||||||
|
|
||||||
// const Fast ModeEnum = 2
|
|
||||||
|
|
||||||
// const Normal ModeEnum = 0
|
|
||||||
|
|
||||||
// const Safe ModeEnum = 1
|
|
||||||
|
|
||||||
// SaveOption is an enum for options when closing a document.
|
// SaveOption is an enum for options when closing a document.
|
||||||
type SaveOption int
|
type SaveOption int
|
||||||
|
|
||||||
func (p *SaveOption) String() string {
|
|
||||||
return fmt.Sprint("", *p) // TODO: Fix
|
|
||||||
}
|
|
||||||
|
|
||||||
// SaveChanges Saves changes before closing documents.
|
// SaveChanges Saves changes before closing documents.
|
||||||
const SaveChanges SaveOption = 1
|
const SaveChanges SaveOption = 1
|
||||||
|
|
||||||
|
|||||||
51
artlayer.go
51
artlayer.go
@@ -5,6 +5,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/sbrow/ps/runner"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ArtLayer reflects some values from an Art Layer in a Photoshop document.
|
// ArtLayer reflects some values from an Art Layer in a Photoshop document.
|
||||||
@@ -32,10 +34,10 @@ func (a *ArtLayer) Bounds() [2][2]int {
|
|||||||
type ArtLayerJSON struct {
|
type ArtLayerJSON struct {
|
||||||
Name string
|
Name string
|
||||||
Bounds [2][2]int
|
Bounds [2][2]int
|
||||||
Visible bool
|
|
||||||
Color [3]int
|
Color [3]int
|
||||||
Stroke [3]int
|
Stroke [3]int
|
||||||
StrokeAmt float32
|
StrokeAmt float32
|
||||||
|
Visible bool
|
||||||
TextItem *TextItem
|
TextItem *TextItem
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,6 +55,7 @@ func (a *ArtLayer) MarshalJSON() ([]byte, error) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON loads json data into the object.
|
||||||
func (a *ArtLayer) UnmarshalJSON(b []byte) error {
|
func (a *ArtLayer) UnmarshalJSON(b []byte) error {
|
||||||
tmp := &ArtLayerJSON{}
|
tmp := &ArtLayerJSON{}
|
||||||
if err := json.Unmarshal(b, &tmp); err != nil {
|
if err := json.Unmarshal(b, &tmp); err != nil {
|
||||||
@@ -71,6 +74,7 @@ func (a *ArtLayer) UnmarshalJSON(b []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Name returns the layer's name.
|
||||||
func (a *ArtLayer) Name() string {
|
func (a *ArtLayer) Name() string {
|
||||||
return a.name
|
return a.name
|
||||||
}
|
}
|
||||||
@@ -100,6 +104,7 @@ func (a *ArtLayer) Y2() int {
|
|||||||
return a.bounds[1][1]
|
return a.bounds[1][1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetParent sets Group c to be the group that holds this layer.
|
||||||
func (a *ArtLayer) SetParent(c Group) {
|
func (a *ArtLayer) SetParent(c Group) {
|
||||||
a.parent = c
|
a.parent = c
|
||||||
}
|
}
|
||||||
@@ -108,7 +113,7 @@ func (a *ArtLayer) SetParent(c Group) {
|
|||||||
// Layers need to be active to perform certain operations
|
// Layers need to be active to perform certain operations
|
||||||
func (a *ArtLayer) SetActive() ([]byte, error) {
|
func (a *ArtLayer) SetActive() ([]byte, error) {
|
||||||
js := fmt.Sprintf("app.activeDocument.activeLayer=%s", JSLayer(a.Path()))
|
js := fmt.Sprintf("app.activeDocument.activeLayer=%s", JSLayer(a.Path()))
|
||||||
return DoJs("compilejs.jsx", js)
|
return DoJS("compilejs.jsx", js)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetColor creates a color overlay for the layer
|
// SetColor creates a color overlay for the layer
|
||||||
@@ -137,7 +142,7 @@ func (a *ArtLayer) SetColor(c Color) {
|
|||||||
log.Println(a.Path())
|
log.Println(a.Path())
|
||||||
log.Panic(err)
|
log.Panic(err)
|
||||||
}
|
}
|
||||||
byt, err = run("colorLayer", fmt.Sprint(r), fmt.Sprint(g), fmt.Sprint(b))
|
byt, err = runner.Run("colorLayer", fmt.Sprint(r), fmt.Sprint(g), fmt.Sprint(b))
|
||||||
if len(byt) != 0 {
|
if len(byt) != 0 {
|
||||||
log.Println(string(byt), "err")
|
log.Println(string(byt), "err")
|
||||||
}
|
}
|
||||||
@@ -146,6 +151,9 @@ func (a *ArtLayer) SetColor(c Color) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetStroke edits the "aura" around the layer. If a nil stroke is given,
|
||||||
|
// The current stroke is removed. If a non-nil stroke is given and the
|
||||||
|
// current stroke is nil, a stroke is added.
|
||||||
func (a *ArtLayer) SetStroke(stk Stroke, fill Color) {
|
func (a *ArtLayer) SetStroke(stk Stroke, fill Color) {
|
||||||
if stk.Size == 0 {
|
if stk.Size == 0 {
|
||||||
a.Stroke = &stk
|
a.Stroke = &stk
|
||||||
@@ -176,7 +184,7 @@ func (a *ArtLayer) SetStroke(stk Stroke, fill Color) {
|
|||||||
col := fill.RGB()
|
col := fill.RGB()
|
||||||
log.Printf("Setting layer %s stroke to %.2fpt %v and color to %v\n", a.name, a.Stroke.Size,
|
log.Printf("Setting layer %s stroke to %.2fpt %v and color to %v\n", a.name, a.Stroke.Size,
|
||||||
a.Stroke.Color.RGB(), a.Color.RGB())
|
a.Stroke.Color.RGB(), a.Color.RGB())
|
||||||
byt, err = run("colorStroke", fmt.Sprint(col[0]), fmt.Sprint(col[1]), fmt.Sprint(col[2]),
|
byt, err = runner.Run("colorStroke", fmt.Sprint(col[0]), fmt.Sprint(col[1]), fmt.Sprint(col[2]),
|
||||||
fmt.Sprintf("%.2f", stk.Size), fmt.Sprint(stkCol[0]), fmt.Sprint(stkCol[1]), fmt.Sprint(stkCol[2]))
|
fmt.Sprintf("%.2f", stk.Size), fmt.Sprint(stkCol[0]), fmt.Sprint(stkCol[1]), fmt.Sprint(stkCol[2]))
|
||||||
if len(byt) != 0 {
|
if len(byt) != 0 {
|
||||||
log.Println(string(byt))
|
log.Println(string(byt))
|
||||||
@@ -186,25 +194,11 @@ func (a *ArtLayer) SetStroke(stk Stroke, fill Color) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Path returns the Path to this layer, through all of its parents.
|
||||||
func (a *ArtLayer) Path() string {
|
func (a *ArtLayer) Path() string {
|
||||||
return fmt.Sprintf("%s%s", a.parent.Path(), a.name)
|
return fmt.Sprintf("%s%s", a.parent.Path(), a.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Layer returns an ArtLayer from the active document given a specified
|
|
||||||
// path string.
|
|
||||||
func layer(path string) (ArtLayer, error) {
|
|
||||||
byt, err := DoJs("getLayer.jsx", JSLayer(path))
|
|
||||||
if err != nil {
|
|
||||||
return ArtLayer{}, err
|
|
||||||
}
|
|
||||||
var out ArtLayer
|
|
||||||
err = json.Unmarshal(byt, &out)
|
|
||||||
if err != nil {
|
|
||||||
return ArtLayer{}, err
|
|
||||||
}
|
|
||||||
return out, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetVisible makes the layer visible.
|
// SetVisible makes the layer visible.
|
||||||
func (a *ArtLayer) SetVisible(b bool) {
|
func (a *ArtLayer) SetVisible(b bool) {
|
||||||
if a.visible == b {
|
if a.visible == b {
|
||||||
@@ -219,7 +213,9 @@ func (a *ArtLayer) SetVisible(b bool) {
|
|||||||
}
|
}
|
||||||
js := fmt.Sprintf("%s.visible=%v;",
|
js := fmt.Sprintf("%s.visible=%v;",
|
||||||
strings.TrimRight(JSLayer(a.Path()), ";"), b)
|
strings.TrimRight(JSLayer(a.Path()), ";"), b)
|
||||||
DoJs("compilejs.jsx", js)
|
if byt, err := DoJS("compilejs.jsx", js); err != nil {
|
||||||
|
log.Println(string(byt), err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Visible returns whether or not the layer is currently hidden.
|
// Visible returns whether or not the layer is currently hidden.
|
||||||
@@ -250,8 +246,11 @@ func (a *ArtLayer) SetPos(x, y int, bound string) {
|
|||||||
default:
|
default:
|
||||||
lyrX = a.X1()
|
lyrX = a.X1()
|
||||||
}
|
}
|
||||||
byt, err := DoJs("moveLayer.jsx", JSLayer(a.Path()), fmt.Sprint(x-lyrX), fmt.Sprint(y-lyrY))
|
byt, err := DoJS("moveLayer.jsx", JSLayer(a.Path()),
|
||||||
|
fmt.Sprint(x-lyrX), fmt.Sprint(y-lyrY),
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
fmt.Printf("%+v %+v\n", a.Parent(), a.Path())
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
var lyr ArtLayer
|
var lyr ArtLayer
|
||||||
@@ -262,9 +261,15 @@ func (a *ArtLayer) SetPos(x, y int, bound string) {
|
|||||||
a.bounds = lyr.bounds
|
a.bounds = lyr.bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Refresh syncs the layer with Photoshop.
|
||||||
func (a *ArtLayer) Refresh() error {
|
func (a *ArtLayer) Refresh() error {
|
||||||
tmp, err := layer(a.Path())
|
var tmp *ArtLayer
|
||||||
if err != nil {
|
data, err := DoJS("getLayer.jsx", JSLayer(a.Path()))
|
||||||
|
if err != nil && len(err.Error()) > 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = json.Unmarshal(data, &tmp); err != nil {
|
||||||
|
fmt.Println(string(data))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
tmp.SetParent(a.Parent())
|
tmp.SetParent(a.Parent())
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package ps
|
|||||||
|
|
||||||
import "encoding/hex"
|
import "encoding/hex"
|
||||||
|
|
||||||
|
// Some basic colors.
|
||||||
var (
|
var (
|
||||||
ColorBlack Color = RGB{0, 0, 0}
|
ColorBlack Color = RGB{0, 0, 0}
|
||||||
ColorGray Color = RGB{128, 128, 128}
|
ColorGray Color = RGB{128, 128, 128}
|
||||||
@@ -41,15 +42,18 @@ func (r RGB) RGB() [3]int {
|
|||||||
return [3]int{r.Red, r.Green, r.Blue}
|
return [3]int{r.Red, r.Green, r.Blue}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hex returns the color coverted to hexidecimal format.
|
||||||
func (r RGB) Hex() []uint8 {
|
func (r RGB) Hex() []uint8 {
|
||||||
src := []uint8{uint8(r.Red), uint8(r.Green), uint8(r.Blue)}
|
src := []uint8{uint8(r.Red), uint8(r.Green), uint8(r.Blue)}
|
||||||
hex := make([]byte, hex.EncodedLen(len(src)))
|
hex := make([]byte, hex.EncodedLen(len(src)))
|
||||||
return hex
|
return hex
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hex is a color in hexadecimal format. It implements the Color interface.
|
// Hex is a color in hexadecimal format.
|
||||||
|
// It satisfies the Color interface.
|
||||||
type Hex []uint8
|
type Hex []uint8
|
||||||
|
|
||||||
|
// RGB returns the Hex value converted to RGB
|
||||||
func (h Hex) RGB() [3]int {
|
func (h Hex) RGB() [3]int {
|
||||||
src := []byte(h)
|
src := []byte(h)
|
||||||
dst := make([]byte, hex.DecodedLen(len(src)))
|
dst := make([]byte, hex.DecodedLen(len(src)))
|
||||||
@@ -60,6 +64,8 @@ func (h Hex) RGB() [3]int {
|
|||||||
return [3]int{int(dst[0]), int(dst[1]), int(dst[2])}
|
return [3]int{int(dst[0]), int(dst[1]), int(dst[2])}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hex returns the hex value of the number,
|
||||||
|
// to satisfy the Color interface.
|
||||||
func (h Hex) Hex() []uint8 {
|
func (h Hex) Hex() []uint8 {
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|||||||
60
document.go
60
document.go
@@ -2,7 +2,6 @@ package ps
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
@@ -20,6 +19,8 @@ type Document struct {
|
|||||||
layerSets []*LayerSet
|
layerSets []*LayerSet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DocumentJSON is an exported version of Document that
|
||||||
|
// allows Documents to be saved to and loaded from JSON.
|
||||||
type DocumentJSON struct {
|
type DocumentJSON struct {
|
||||||
Name string
|
Name string
|
||||||
Height int
|
Height int
|
||||||
@@ -28,11 +29,13 @@ type DocumentJSON struct {
|
|||||||
LayerSets []*LayerSet
|
LayerSets []*LayerSet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalJSON returns the Document in JSON format.
|
||||||
func (d *Document) MarshalJSON() ([]byte, error) {
|
func (d *Document) MarshalJSON() ([]byte, error) {
|
||||||
return json.Marshal(&DocumentJSON{Name: d.name, Height: d.height,
|
return json.Marshal(&DocumentJSON{Name: d.name, Height: d.height,
|
||||||
Width: d.width, ArtLayers: d.artLayers, LayerSets: d.layerSets})
|
Width: d.width, ArtLayers: d.artLayers, LayerSets: d.layerSets})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON loads JSON data into this Document.
|
||||||
func (d *Document) UnmarshalJSON(b []byte) error {
|
func (d *Document) UnmarshalJSON(b []byte) error {
|
||||||
tmp := &DocumentJSON{}
|
tmp := &DocumentJSON{}
|
||||||
if err := json.Unmarshal(b, &tmp); err != nil {
|
if err := json.Unmarshal(b, &tmp); err != nil {
|
||||||
@@ -58,15 +61,17 @@ func (d *Document) Name() string {
|
|||||||
return d.name
|
return d.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parent returns the Group that contains d.
|
||||||
func (d *Document) Parent() Group {
|
func (d *Document) Parent() Group {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// The height of the document, in pixels.
|
// Height returns the height of the document, in pixels.
|
||||||
func (d *Document) Height() int {
|
func (d *Document) Height() int {
|
||||||
return d.height
|
return d.height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ArtLayers returns this document's ArtLayers, if any.
|
||||||
func (d *Document) ArtLayers() []*ArtLayer {
|
func (d *Document) ArtLayers() []*ArtLayer {
|
||||||
return d.artLayers
|
return d.artLayers
|
||||||
}
|
}
|
||||||
@@ -82,7 +87,9 @@ func (d *Document) LayerSet(name string) *LayerSet {
|
|||||||
for _, set := range d.layerSets {
|
for _, set := range d.layerSets {
|
||||||
if set.name == name {
|
if set.name == name {
|
||||||
if Mode != Fast && !set.current {
|
if Mode != Fast && !set.current {
|
||||||
set.Refresh()
|
if err := set.Refresh(); err != nil {
|
||||||
|
log.Panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return set
|
return set
|
||||||
}
|
}
|
||||||
@@ -90,41 +97,38 @@ func (d *Document) LayerSet(name string) *LayerSet {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ActiveDocument returns document currently focused in Photoshop.
|
||||||
|
//
|
||||||
|
// TODO: Reduce cylcomatic complexity
|
||||||
func ActiveDocument() (*Document, error) {
|
func ActiveDocument() (*Document, error) {
|
||||||
log.Println("Loading ActiveDoucment")
|
log.Println("Loading ActiveDoucment")
|
||||||
d := &Document{}
|
d := &Document{}
|
||||||
|
|
||||||
byt, err := DoJs("activeDocName.jsx")
|
byt, err := DoJS("activeDocName.jsx")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
d.name = strings.TrimRight(string(byt), "\r\n")
|
d.name = strings.TrimRight(string(byt), "\r\n")
|
||||||
if Mode != Safe {
|
if Mode != Safe {
|
||||||
byt, err = ioutil.ReadFile(d.Filename())
|
err = d.Restore()
|
||||||
if err == nil {
|
return d, err
|
||||||
log.Println("Previous version found, loading")
|
|
||||||
err = json.Unmarshal(byt, &d)
|
|
||||||
if err == nil {
|
|
||||||
return d, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
log.Println("Loading manually (This could take awhile)")
|
log.Println("Loading manually (This could take awhile)")
|
||||||
byt, err = DoJs("getActiveDoc.jsx")
|
byt, err = DoJS("getActiveDoc.jsx")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panic(err)
|
log.Panic(err)
|
||||||
}
|
}
|
||||||
err = json.Unmarshal(byt, &d)
|
err = json.Unmarshal(byt, &d)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
d.Dump()
|
d.Dump()
|
||||||
fmt.Println(string(byt))
|
|
||||||
log.Panic(err)
|
log.Panic(err)
|
||||||
}
|
}
|
||||||
for _, lyr := range d.artLayers {
|
for _, lyr := range d.artLayers {
|
||||||
lyr.SetParent(d)
|
lyr.SetParent(d)
|
||||||
}
|
}
|
||||||
for i, set := range d.layerSets {
|
for i, set := range d.layerSets {
|
||||||
s, err := NewLayerSet(set.Path()+"/", d)
|
var s *LayerSet
|
||||||
|
s, err = NewLayerSet(set.Path()+"/", d)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -135,8 +139,22 @@ func ActiveDocument() (*Document, error) {
|
|||||||
return d, err
|
return d, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Restore loads document data from a JSON file.
|
||||||
|
func (d *Document) Restore() error {
|
||||||
|
byt, err := ioutil.ReadFile(d.Filename())
|
||||||
|
if err == nil {
|
||||||
|
log.Println("Previous version found, loading")
|
||||||
|
err = json.Unmarshal(byt, &d)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetParent does nothing, as the document is a top-level object
|
||||||
|
// and therefore can't have a parent group.
|
||||||
|
// The function is needed to implement the group interface.
|
||||||
func (d *Document) SetParent(g Group) {}
|
func (d *Document) SetParent(g Group) {}
|
||||||
|
|
||||||
|
// Path returns the root path ("") for all the layers.
|
||||||
func (d *Document) Path() string {
|
func (d *Document) Path() string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@@ -151,16 +169,24 @@ func (d *Document) Filename() string {
|
|||||||
strings.TrimRight(d.name, "\r\n")+".txt")
|
strings.TrimRight(d.name, "\r\n")+".txt")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dump saves the document to disk in JSON format.
|
||||||
func (d *Document) Dump() {
|
func (d *Document) Dump() {
|
||||||
log.Println("Dumping to disk")
|
log.Println("Dumping to disk")
|
||||||
|
log.Println(d.Filename())
|
||||||
f, err := os.Create(d.Filename())
|
f, err := os.Create(d.Filename())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer func() {
|
||||||
|
if err = f.Close(); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
byt, err := json.MarshalIndent(d, "", "\t")
|
byt, err := json.MarshalIndent(d, "", "\t")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
f.Write(byt)
|
if _, err = f.Write(byt); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import "fmt"
|
|||||||
|
|
||||||
func ExampleJSLayer() {
|
func ExampleJSLayer() {
|
||||||
// The path of a layer inside a top level group.
|
// The path of a layer inside a top level group.
|
||||||
path := "/Group 1/Layer 1"
|
path := "Group 1/Layer 1"
|
||||||
fmt.Println(JSLayer(path))
|
fmt.Println(JSLayer(path))
|
||||||
// Output: app.activeDocument.layerSets.getByName('Group 1').artLayers.getByName('Layer 1');
|
// Output: app.activeDocument.layerSets.getByName('Group 1').artLayers.getByName('Layer 1');
|
||||||
}
|
}
|
||||||
|
|||||||
100
layerset.go
100
layerset.go
@@ -1,4 +1,3 @@
|
|||||||
// TODO: Count skipped steps.
|
|
||||||
package ps
|
package ps
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -21,6 +20,7 @@ type Group interface {
|
|||||||
UnmarshalJSON(b []byte) error
|
UnmarshalJSON(b []byte) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LayerSet holds a group of Layer objects and a group of LayerSet objects.
|
||||||
type LayerSet struct {
|
type LayerSet struct {
|
||||||
name string
|
name string
|
||||||
bounds [2][2]int
|
bounds [2][2]int
|
||||||
@@ -31,6 +31,8 @@ type LayerSet struct {
|
|||||||
layerSets []*LayerSet
|
layerSets []*LayerSet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LayerSetJSON is an exported version of LayerSet, that allows LayerSets to be
|
||||||
|
// saved to and loaded from JSON.
|
||||||
type LayerSetJSON struct {
|
type LayerSetJSON struct {
|
||||||
Name string
|
Name string
|
||||||
Bounds [2][2]int
|
Bounds [2][2]int
|
||||||
@@ -39,6 +41,7 @@ type LayerSetJSON struct {
|
|||||||
LayerSets []*LayerSet
|
LayerSets []*LayerSet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalJSON returns the LayerSet in JSON form.
|
||||||
func (l *LayerSet) MarshalJSON() ([]byte, error) {
|
func (l *LayerSet) MarshalJSON() ([]byte, error) {
|
||||||
return json.Marshal(&LayerSetJSON{
|
return json.Marshal(&LayerSetJSON{
|
||||||
Name: l.name,
|
Name: l.name,
|
||||||
@@ -49,6 +52,7 @@ func (l *LayerSet) MarshalJSON() ([]byte, error) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON loads the json data into this LayerSet.
|
||||||
func (l *LayerSet) UnmarshalJSON(b []byte) error {
|
func (l *LayerSet) UnmarshalJSON(b []byte) error {
|
||||||
tmp := &LayerSetJSON{}
|
tmp := &LayerSetJSON{}
|
||||||
if err := json.Unmarshal(b, &tmp); err != nil {
|
if err := json.Unmarshal(b, &tmp); err != nil {
|
||||||
@@ -69,15 +73,20 @@ func (l *LayerSet) UnmarshalJSON(b []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *LayerSet) Name() string {
|
// Name returns the name of the LayerSet
|
||||||
|
func (l LayerSet) Name() string {
|
||||||
return l.name
|
return l.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ArtLayers returns the LayerSet's ArtLayers.
|
||||||
func (l *LayerSet) ArtLayers() []*ArtLayer {
|
func (l *LayerSet) ArtLayers() []*ArtLayer {
|
||||||
if Mode != 2 {
|
if Mode != Fast {
|
||||||
for _, lyr := range l.artLayers {
|
for _, lyr := range l.artLayers {
|
||||||
if !lyr.current {
|
if !lyr.current {
|
||||||
lyr.Refresh()
|
if err := lyr.Refresh(); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
lyr.current = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,9 +102,10 @@ func (l *LayerSet) ArtLayer(name string) *ArtLayer {
|
|||||||
if Mode == 0 && !lyr.current {
|
if Mode == 0 && !lyr.current {
|
||||||
err := lyr.Refresh()
|
err := lyr.Refresh()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Refresh()
|
if err = l.Refresh(); err != nil {
|
||||||
err := lyr.Refresh()
|
log.Panic(err)
|
||||||
if err != nil {
|
}
|
||||||
|
if err := lyr.Refresh(); err != nil {
|
||||||
log.Panic(err)
|
log.Panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,18 +113,16 @@ func (l *LayerSet) ArtLayer(name string) *ArtLayer {
|
|||||||
return lyr
|
return lyr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// l.Refresh()
|
|
||||||
// for _, lyr := range l.artLayers {
|
|
||||||
// fmt.Println(lyr)
|
|
||||||
// }
|
|
||||||
lyr := l.ArtLayer(name)
|
lyr := l.ArtLayer(name)
|
||||||
fmt.Println(lyr)
|
fmt.Println(lyr)
|
||||||
if lyr == nil {
|
if lyr == nil {
|
||||||
log.Panic(errors.New("Layer not found!"))
|
log.Panic(errors.New("layer not found"))
|
||||||
}
|
}
|
||||||
return lyr
|
return lyr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LayerSets returns the LayerSets contained within
|
||||||
|
// this set.
|
||||||
func (l *LayerSet) LayerSets() []*LayerSet {
|
func (l *LayerSet) LayerSets() []*LayerSet {
|
||||||
return l.layerSets
|
return l.layerSets
|
||||||
}
|
}
|
||||||
@@ -131,18 +139,21 @@ func (l *LayerSet) LayerSet(name string) *LayerSet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Bounds returns the furthest corners of the LayerSet.
|
// Bounds returns the furthest corners of the LayerSet.
|
||||||
func (l *LayerSet) Bounds() [2][2]int {
|
func (l LayerSet) Bounds() [2][2]int {
|
||||||
return l.bounds
|
return l.bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *LayerSet) SetParent(c Group) {
|
// SetParent puts this LayerSet into the given group.
|
||||||
l.parent = c
|
func (l *LayerSet) SetParent(g Group) {
|
||||||
|
l.parent = g
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parent returns this layerSet's parent.
|
||||||
func (l *LayerSet) Parent() Group {
|
func (l *LayerSet) Parent() Group {
|
||||||
return l.parent
|
return l.parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Path returns the layer path to this Set.
|
||||||
func (l *LayerSet) Path() string {
|
func (l *LayerSet) Path() string {
|
||||||
if l.parent == nil {
|
if l.parent == nil {
|
||||||
return l.name
|
return l.name
|
||||||
@@ -150,29 +161,29 @@ func (l *LayerSet) Path() string {
|
|||||||
return fmt.Sprintf("%s%s/", l.parent.Path(), l.name)
|
return fmt.Sprintf("%s%s/", l.parent.Path(), l.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewLayerSet grabs the LayerSet with the given path and returns it.
|
||||||
func NewLayerSet(path string, g Group) (*LayerSet, error) {
|
func NewLayerSet(path string, g Group) (*LayerSet, error) {
|
||||||
path = strings.Replace(path, "//", "/", -1)
|
path = strings.Replace(path, "//", "/", -1)
|
||||||
byt, err := DoJs("getLayerSet.jsx", JSLayer(path))
|
byt, err := DoJS("getLayerSet.jsx", JSLayer(path), JSLayerMerge(path))
|
||||||
|
fmt.Println(JSLayer(path), JSLayerMerge(path))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panic(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
var out *LayerSet
|
var out *LayerSet
|
||||||
err = json.Unmarshal(byt, &out)
|
err = json.Unmarshal(byt, &out)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(JSLayer(path))
|
log.Println(JSLayer(path))
|
||||||
log.Println(string(byt))
|
log.Println(string(byt))
|
||||||
log.Panic(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
out.SetParent(g)
|
out.SetParent(g)
|
||||||
log.Printf("Loading ActiveDocument/%s\n", out.Path())
|
log.Printf("Loading ActiveDocument/%s\n", out.Path())
|
||||||
if err != nil {
|
|
||||||
return &LayerSet{}, err
|
|
||||||
}
|
|
||||||
for _, lyr := range out.artLayers {
|
for _, lyr := range out.artLayers {
|
||||||
lyr.SetParent(out)
|
lyr.SetParent(out)
|
||||||
}
|
}
|
||||||
for i, set := range out.layerSets {
|
for i, set := range out.layerSets {
|
||||||
s, err := NewLayerSet(fmt.Sprintf("%s%s/", path, set.Name()), out)
|
var s *LayerSet
|
||||||
|
s, err = NewLayerSet(fmt.Sprintf("%s%s/", path, set.Name()), out)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -183,19 +194,23 @@ func NewLayerSet(path string, g Group) (*LayerSet, error) {
|
|||||||
return out, err
|
return out, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *LayerSet) Visible() bool {
|
// Visible returns whether or not the LayerSet is currently visible.
|
||||||
|
func (l LayerSet) Visible() bool {
|
||||||
return l.visible
|
return l.visible
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetVisible makes the LayerSet visible.
|
// SetVisible makes the LayerSet visible.
|
||||||
func (l *LayerSet) SetVisible(b bool) {
|
func (l *LayerSet) SetVisible(b bool) error {
|
||||||
if l.visible == b {
|
if l.visible == b {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
js := fmt.Sprintf("%s.visible=%v;", strings.TrimRight(
|
js := fmt.Sprintf("%s.visible=%v;", strings.TrimRight(
|
||||||
JSLayer(l.Path()), ";"), b)
|
JSLayer(l.Path()), ";"), b)
|
||||||
DoJs("compilejs.jsx", js)
|
if _, err := DoJS("compilejs.jsx", js); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
l.visible = b
|
l.visible = b
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetPos snaps the given layerset boundry to the given point.
|
// SetPos snaps the given layerset boundry to the given point.
|
||||||
@@ -204,8 +219,9 @@ func (l *LayerSet) SetPos(x, y int, bound string) {
|
|||||||
if !l.visible || (x == 0 && y == 0) {
|
if !l.visible || (x == 0 && y == 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
byt, err := DoJs("LayerSetBounds.jsx", JSLayer(l.Path()),
|
path := JSLayer(l.Path())
|
||||||
JSLayer(l.Path(), true))
|
mrgPath := JSLayerMerge(l.Path())
|
||||||
|
byt, err := DoJS("LayerSetBounds.jsx", path, mrgPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(string(byt))
|
log.Println(string(byt))
|
||||||
log.Panic(err)
|
log.Panic(err)
|
||||||
@@ -234,45 +250,53 @@ func (l *LayerSet) SetPos(x, y int, bound string) {
|
|||||||
default:
|
default:
|
||||||
lyrX = l.bounds[0][0]
|
lyrX = l.bounds[0][0]
|
||||||
}
|
}
|
||||||
byt, err = DoJs("moveLayer.jsx", JSLayer(l.Path()), fmt.Sprint(x-lyrX),
|
byt, err = DoJS("moveLayer.jsx", JSLayer(l.Path()), fmt.Sprint(x-lyrX),
|
||||||
fmt.Sprint(y-lyrY), JSLayer(l.Path(), true))
|
fmt.Sprint(y-lyrY), JSLayerMerge(l.Path()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("byte:", string(byt))
|
fmt.Println("byte:", string(byt))
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
var lyr LayerSet
|
var lyr LayerSet
|
||||||
err = json.Unmarshal(byt, &lyr)
|
if err = json.Unmarshal(byt, &lyr); err != nil {
|
||||||
if err != nil {
|
|
||||||
fmt.Println("byte:", string(byt))
|
fmt.Println("byte:", string(byt))
|
||||||
log.Panic(err)
|
log.Panic(err)
|
||||||
}
|
}
|
||||||
l.bounds = lyr.bounds
|
l.bounds = lyr.bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *LayerSet) Refresh() {
|
// Refresh syncs the LayerSet with Photoshop.
|
||||||
|
func (l *LayerSet) Refresh() error {
|
||||||
var tmp *LayerSet
|
var tmp *LayerSet
|
||||||
byt, err := DoJs("getLayerSet.jsx", JSLayer(l.Path()), JSLayer(l.Path(), true))
|
byt, err := DoJS("getLayerSet.jsx", JSLayer(l.Path()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
if err = DoAction("Undo", "DK"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if byt, err = DoJS("getLayerSet.jsx", JSLayer(l.Path())); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
err = json.Unmarshal(byt, &tmp)
|
err = json.Unmarshal(byt, &tmp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Error in LayerSet.Refresh() \"", string(byt), "\"", "for", l.Path())
|
log.Println("Error in LayerSet.Refresh() \"", string(byt), "\"", "for", l.Path())
|
||||||
log.Panic(err)
|
return err
|
||||||
}
|
}
|
||||||
tmp.SetParent(l.Parent())
|
tmp.SetParent(l.Parent())
|
||||||
for _, lyr := range l.artLayers {
|
for _, lyr := range l.artLayers {
|
||||||
err := lyr.Refresh()
|
err = lyr.Refresh()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.artLayers = tmp.artLayers
|
l.artLayers = tmp.artLayers
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, set := range l.layerSets {
|
for _, set := range l.layerSets {
|
||||||
set.Refresh()
|
if err = set.Refresh(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
l.name = tmp.name
|
l.name = tmp.name
|
||||||
l.bounds = tmp.bounds
|
l.bounds = tmp.bounds
|
||||||
l.visible = tmp.visible
|
l.visible = tmp.visible
|
||||||
l.current = true
|
l.current = true
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
124
ps.go
124
ps.go
@@ -2,40 +2,28 @@
|
|||||||
// The interaction between the two is implemented using Javascript/VBScript.
|
// The interaction between the two is implemented using Javascript/VBScript.
|
||||||
//
|
//
|
||||||
// Currently only supports Photoshop CS5 Windows x86_64.
|
// Currently only supports Photoshop CS5 Windows x86_64.
|
||||||
//
|
//go:generate godocdown -output=README.md
|
||||||
// TODO: Create a Photoshop struct to hold program values and functions.
|
|
||||||
package ps
|
package ps
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/sbrow/ps/runner"
|
||||||
)
|
)
|
||||||
|
|
||||||
// opts are the options we need to pass to scmd.
|
// The full path to this directory.
|
||||||
var opts string
|
|
||||||
|
|
||||||
// scmd is the name of the program that runs scripts on this OS.
|
|
||||||
var scmd string
|
|
||||||
|
|
||||||
var pkgpath string
|
var pkgpath string
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
_, file, _, _ := runtime.Caller(0)
|
_, file, _, _ := runtime.Caller(0)
|
||||||
pkgpath = filepath.Dir(file)
|
pkgpath = filepath.Dir(file)
|
||||||
switch runtime.GOOS {
|
|
||||||
case "windows":
|
|
||||||
scmd = "cscript.exe"
|
|
||||||
opts = "/nologo"
|
|
||||||
case "darwin":
|
|
||||||
scmd = "osacript"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApplyDataset fills out a template file with information
|
// ApplyDataset fills out a template file with information
|
||||||
@@ -44,47 +32,60 @@ func init() {
|
|||||||
// data in the Go Document struct- you will have to implement syncing
|
// data in the Go Document struct- you will have to implement syncing
|
||||||
// them yourself.
|
// them yourself.
|
||||||
func ApplyDataset(name string) error {
|
func ApplyDataset(name string) error {
|
||||||
_, err := DoJs("applyDataset.jsx", name)
|
_, err := DoJS("applyDataset.jsx", name)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes the active document in Photoshop, using the given save option.
|
// Close closes the active document in Photoshop, using the given save option.
|
||||||
// TODO: Move to Document
|
// TODO: Move to Document
|
||||||
func Close(save SaveOption) error {
|
func Close(save SaveOption) error {
|
||||||
_, err := run("close", save.String())
|
_, err := runner.Run("close", fmt.Sprint(save))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// DoAction runs the Photoshop Action with the given name from the Action Set "from".
|
// DoAction runs the Photoshop Action with the given name from the Action Set "from".
|
||||||
func DoAction(action, from string) error {
|
func DoAction(action, from string) error {
|
||||||
_, err := run("action", action, from)
|
_, err := runner.Run("action", action, from)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// DoJs runs a Photoshop Javascript script file (.jsx) from the specified location.
|
// DoJS runs a Photoshop Javascript script file (.jsx) from the specified location.
|
||||||
// The script can't directly return output, so instead it writes output to
|
// The script can't directly return output, so instead it writes output to
|
||||||
// a temporary file ($TEMP/js_out.txt), whose contents is then read and returned.
|
// a temporary file ($TEMP/js_out.txt), whose contents is then read and returned.
|
||||||
func DoJs(path string, args ...string) (out []byte, err error) {
|
func DoJS(path string, args ...string) ([]byte, error) {
|
||||||
|
// var err error
|
||||||
// Temp file for js to output to.
|
// Temp file for js to output to.
|
||||||
outpath := filepath.Join(os.Getenv("TEMP"), "js_out.txt")
|
outpath, err := ioutil.TempFile(os.Getenv("TEMP"), "")
|
||||||
// defer os.Remove(outpath)
|
defer func() {
|
||||||
|
if err = outpath.Close(); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
if err = os.Remove(outpath.Name()); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
if !strings.HasSuffix(path, ".jsx") {
|
if !strings.HasSuffix(path, ".jsx") {
|
||||||
path += ".jsx"
|
path += ".jsx"
|
||||||
}
|
}
|
||||||
|
|
||||||
args = append([]string{outpath}, args...)
|
args = append([]string{outpath.Name()}, args...)
|
||||||
|
|
||||||
// If passed a script by name, assume it's in the default folder.
|
// If passed a script by name, assume it's in the default folder.
|
||||||
if filepath.Dir(path) == "." {
|
if filepath.Dir(path) == "." {
|
||||||
path = filepath.Join(pkgpath, "scripts", path)
|
path = filepath.Join(pkgpath, "runner", "scripts", path)
|
||||||
}
|
}
|
||||||
|
|
||||||
args = append([]string{path}, args...)
|
args = append([]string{path}, args...)
|
||||||
cmd, err := run("dojs", args...)
|
cmd, err := runner.Run("dojs", args...)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
file, err := ioutil.ReadFile(outpath)
|
var data []byte
|
||||||
|
data, err = ioutil.ReadFile(outpath.Name())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
cmd = append(cmd, file...)
|
cmd = append(cmd, data...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return cmd, err
|
return cmd, err
|
||||||
@@ -94,7 +95,7 @@ func DoJs(path string, args ...string) (out []byte, err error) {
|
|||||||
//
|
//
|
||||||
// Init should be called before all other
|
// Init should be called before all other
|
||||||
func Init() error {
|
func Init() error {
|
||||||
_, err := run("start")
|
_, err := runner.Run("start")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,15 +104,10 @@ func Init() error {
|
|||||||
// property of the layer, you'll have to trim the output before concatenating.
|
// property of the layer, you'll have to trim the output before concatenating.
|
||||||
//
|
//
|
||||||
// TODO: get rid of the semicolon.
|
// TODO: get rid of the semicolon.
|
||||||
func JSLayer(path string, art ...bool) string {
|
func JSLayer(path string) string {
|
||||||
path = strings.TrimLeft(path, "/")
|
|
||||||
pth := strings.Split(path, "/")
|
pth := strings.Split(path, "/")
|
||||||
js := "app.activeDocument"
|
js := "app.activeDocument"
|
||||||
last := len(pth) - 1
|
last := len(pth) - 1
|
||||||
if len(art) > 0 {
|
|
||||||
pth = pth[:len(pth)-1]
|
|
||||||
last--
|
|
||||||
}
|
|
||||||
if last > 0 {
|
if last > 0 {
|
||||||
for i := 0; i < last; i++ {
|
for i := 0; i < last; i++ {
|
||||||
js += fmt.Sprintf(".layerSets.getByName('%s')", pth[i])
|
js += fmt.Sprintf(".layerSets.getByName('%s')", pth[i])
|
||||||
@@ -123,56 +119,33 @@ func JSLayer(path string, art ...bool) string {
|
|||||||
return js + ";"
|
return js + ";"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// JSLayerMerge gets the Javascript code to get the Layer or LayerSet with this path
|
||||||
|
// and returns the result if you were to merge the bottom-most LayerSet.
|
||||||
|
//
|
||||||
|
// If the bottom-most Object in the path is not a LayerSet, it will returns the same
|
||||||
|
// results as JSLayer.
|
||||||
|
func JSLayerMerge(path string) string {
|
||||||
|
reg := regexp.MustCompile(`layerSets(\.getByName\('[^']*'\)($|[^.]))`)
|
||||||
|
return reg.ReplaceAllString(JSLayer(path), "artLayers$1")
|
||||||
|
}
|
||||||
|
|
||||||
// Open opens a Photoshop document with the specified path.
|
// Open opens a Photoshop document with the specified path.
|
||||||
// If Photoshop is not currently running, it is started before
|
// If Photoshop is not currently running, it is started before
|
||||||
// opening the document.
|
// opening the document.
|
||||||
func Open(path string) error {
|
func Open(path string) error {
|
||||||
_, err := run("open", path)
|
_, err := runner.Run("open", path)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Quit exits Photoshop, closing all open docuemnts using the given save option.
|
// Quit exits Photoshop, closing all open docuemnts using the given save option.
|
||||||
func Quit(save SaveOption) error {
|
func Quit(save SaveOption) error {
|
||||||
_, err := run("quit", save.String())
|
_, err := runner.Run("quit", fmt.Sprint(save))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// run handles running the script files, returning output, and displaying errors.
|
|
||||||
func run(name string, args ...string) ([]byte, error) {
|
|
||||||
var ext string
|
|
||||||
var out, errs bytes.Buffer
|
|
||||||
|
|
||||||
switch runtime.GOOS {
|
|
||||||
case "windows":
|
|
||||||
ext = ".vbs"
|
|
||||||
case "darwin":
|
|
||||||
ext = ".applescript"
|
|
||||||
}
|
|
||||||
if !strings.HasSuffix(name, ext) {
|
|
||||||
name += ext
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.Contains(name, "dojs") {
|
|
||||||
args = append([]string{opts, filepath.Join(pkgpath, "scripts", name)},
|
|
||||||
args[0],
|
|
||||||
fmt.Sprint(strings.Join(args[1:], ",,")),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
args = append([]string{opts, filepath.Join(pkgpath, "scripts", name)}, args...)
|
|
||||||
}
|
|
||||||
cmd := exec.Command(scmd, args...)
|
|
||||||
cmd.Stdout = &out
|
|
||||||
cmd.Stderr = &errs
|
|
||||||
err := cmd.Run()
|
|
||||||
if err != nil || len(errs.Bytes()) != 0 {
|
|
||||||
return out.Bytes(), errors.New(errs.String())
|
|
||||||
}
|
|
||||||
return out.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SaveAs saves the Photoshop document to the given location.
|
// SaveAs saves the Photoshop document to the given location.
|
||||||
func SaveAs(path string) error {
|
func SaveAs(path string) error {
|
||||||
_, err := run("save", path)
|
_, err := runner.Run("save", path)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,10 +153,11 @@ func SaveAs(path string) error {
|
|||||||
// signals that they are ready to continue (by pushing enter).
|
// signals that they are ready to continue (by pushing enter).
|
||||||
//
|
//
|
||||||
// Useful for when you need to do something by hand in the middle of an
|
// Useful for when you need to do something by hand in the middle of an
|
||||||
// otherwise automated process. (i.e. importing a dataset).
|
// otherwise automated process, (e.g. importing a dataset).
|
||||||
func Wait(msg string) {
|
func Wait(msg string) {
|
||||||
fmt.Print(msg)
|
|
||||||
var input string
|
var input string
|
||||||
|
|
||||||
|
fmt.Print(msg)
|
||||||
fmt.Scanln(&input)
|
fmt.Scanln(&input)
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
}
|
}
|
||||||
|
|||||||
113
ps_test.go
113
ps_test.go
@@ -5,15 +5,28 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/sbrow/ps/runner"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var testDoc string
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Mode = Normal
|
||||||
|
log.Printf("Running in mode %v\n", Mode)
|
||||||
|
testDoc = filepath.Join(pkgpath, "test.psd")
|
||||||
|
}
|
||||||
|
|
||||||
func TestPkgPath(t *testing.T) {
|
func TestPkgPath(t *testing.T) {
|
||||||
out := filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "sbrow", "ps")
|
want := filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "sbrow", "ps")
|
||||||
if filepath.Join(pkgpath) != out {
|
got := filepath.Join(pkgpath)
|
||||||
t.Error(filepath.Join(pkgpath), out)
|
if got != want {
|
||||||
|
t.Errorf("wanted: %s\ngot: %s", want, got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,8 +34,8 @@ func TestInit(t *testing.T) {
|
|||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
t.Skip("Skipping \"TestStart\"")
|
t.Skip("Skipping \"TestStart\"")
|
||||||
}
|
}
|
||||||
err := Init()
|
Quit(2)
|
||||||
if err != nil {
|
if err := Init(); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -31,8 +44,8 @@ func TestOpen(t *testing.T) {
|
|||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
t.Skip("Skipping \"TestOpen\"")
|
t.Skip("Skipping \"TestOpen\"")
|
||||||
}
|
}
|
||||||
err := Open("F:\\GitLab\\dreamkeepers-psd\\Template009.1.psd")
|
if err := Open(testDoc); err != nil {
|
||||||
if err != nil {
|
log.Println(testDoc)
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -51,6 +64,7 @@ func TestQuit(t *testing.T) {
|
|||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
t.Skip("Skipping \"TestQuit\"")
|
t.Skip("Skipping \"TestQuit\"")
|
||||||
}
|
}
|
||||||
|
Init()
|
||||||
err := Quit(2)
|
err := Quit(2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -58,25 +72,25 @@ func TestQuit(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDoJs(t *testing.T) {
|
func TestDoJs(t *testing.T) {
|
||||||
err := Open("F:\\GitLab\\dreamkeepers-psd\\Template009.1.psd")
|
var err error
|
||||||
if err != nil {
|
if err = Open(testDoc); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
want := []byte("F:\\TEMP\\js_out.txt\r\narg\r\nargs\r\n")
|
want := "F:\\\\TEMP\\\\[0-9]*\r\narg\r\nargs\r\n"
|
||||||
script := "test.jsx"
|
script := "test.jsx"
|
||||||
ret, err := DoJs(script, "arg", "args")
|
var got []byte
|
||||||
if err != nil {
|
if got, err = DoJS(script, "arg", "args"); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if string(ret) != string(want) {
|
if b, err := regexp.Match(want, got); err != nil || !b {
|
||||||
fail := fmt.Sprintf("TestJS failed.\ngot:\t\"%s\"\nwant:\t\"%s\"", ret, want)
|
fail := fmt.Sprintf("wanted: %s\ngot: %s err: %s\n", want, got, err)
|
||||||
t.Error(fail)
|
t.Error(fail)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRun(t *testing.T) {
|
func TestRun(t *testing.T) {
|
||||||
out := []byte("hello,\r\nworld!\r\n")
|
out := []byte("hello,\r\nworld!\r\n")
|
||||||
msg, err := run("test", "hello,", "world!")
|
msg, err := runner.Run("test", "hello,", "world!")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -94,13 +108,12 @@ func TestDoAction_Crop(t *testing.T) {
|
|||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
t.Skip("Skipping \"TestDoAction_Crop\"")
|
t.Skip("Skipping \"TestDoAction_Crop\"")
|
||||||
}
|
}
|
||||||
err := Open("F:\\GitLab\\dreamkeepers-psd\\Template009.1.psd")
|
var err error
|
||||||
if err != nil {
|
if err = Open(testDoc); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
err = DoAction("DK", "Crop")
|
if err = DoAction("DK", "Crop"); err != nil {
|
||||||
if err != nil {
|
t.Error(err)
|
||||||
t.Fatal(err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,47 +121,37 @@ func TestDoAction_Undo(t *testing.T) {
|
|||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
t.Skip("Skipping \"TestDoAction_Undo\"")
|
t.Skip("Skipping \"TestDoAction_Undo\"")
|
||||||
}
|
}
|
||||||
err := DoAction("DK", "Undo")
|
if err := DoAction("DK", "Undo"); err != nil {
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSaveAs(t *testing.T) {
|
func TestSaveAs(t *testing.T) {
|
||||||
err := SaveAs("F:\\TEMP\\test.png")
|
if err := SaveAs("F:\\TEMP\\test.png"); err != nil {
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
os.Remove("F:\\TEMP\\test.png")
|
os.Remove("F:\\TEMP\\test.png")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLayerSet(t *testing.T) {
|
func TestLayerSet(t *testing.T) {
|
||||||
_, err := NewLayerSet("Areas/TitleBackground/", nil)
|
if _, err := NewLayerSet("Group 1/", nil); err != nil {
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLayer(t *testing.T) {
|
|
||||||
_, err := layer("Border/Inner Border")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMove(t *testing.T) {
|
func TestMove(t *testing.T) {
|
||||||
lyr, err := layer("Group 1/Layer 1")
|
d, err := ActiveDocument()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
lyr := d.LayerSet("Group 1").ArtLayer("Layer 1")
|
||||||
lyr.SetPos(100, 50, "TL")
|
lyr.SetPos(100, 50, "TL")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestActiveDocument(t *testing.T) {
|
func TestActiveDocument(t *testing.T) {
|
||||||
Mode = Safe
|
|
||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
t.Skip("Skipping \"TestDocument\"")
|
t.Skip("Skipping \"TestDocument\"")
|
||||||
}
|
}
|
||||||
|
Open(testDoc)
|
||||||
d, err := ActiveDocument()
|
d, err := ActiveDocument()
|
||||||
defer d.Dump()
|
defer d.Dump()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -170,8 +173,7 @@ func TestActiveDocument(t *testing.T) {
|
|||||||
fmt.Println(d.layerSets[0].artLayers[0].Parent())
|
fmt.Println(d.layerSets[0].artLayers[0].Parent())
|
||||||
t.Fatal("Layerset's ArtLayers do not have correct parents")
|
t.Fatal("Layerset's ArtLayers do not have correct parents")
|
||||||
}
|
}
|
||||||
// d.LayerSet("Areas").LayerSet("Bottom").ArtLayer("L Bar").SetColor(155, 255, 255)
|
lyr := d.LayerSet("Group 1").ArtLayer("Layer 1")
|
||||||
lyr := d.LayerSet("Text").ArtLayer("speed")
|
|
||||||
if lyr == nil {
|
if lyr == nil {
|
||||||
t.Fatal("lyr does not exist")
|
t.Fatal("lyr does not exist")
|
||||||
}
|
}
|
||||||
@@ -180,7 +182,7 @@ func TestActiveDocument(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestColor(t *testing.T) {
|
func TestColor(t *testing.T) {
|
||||||
byt, err := run("colorLayer.vbs", "255", "255", "255")
|
byt, err := runner.Run("colorLayer.vbs", "255", "255", "255")
|
||||||
fmt.Println(string(byt))
|
fmt.Println(string(byt))
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -204,15 +206,15 @@ func TestDocumentLayerSet(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
set := d.LayerSet("Text")
|
set := d.LayerSet("Group 1")
|
||||||
fmt.Println(set)
|
fmt.Println(set)
|
||||||
for _, lyr := range set.ArtLayers() {
|
for _, lyr := range set.ArtLayers() {
|
||||||
fmt.Println(lyr.name)
|
fmt.Println(lyr.name)
|
||||||
}
|
}
|
||||||
lyr := set.ArtLayer("id")
|
lyr := set.ArtLayer("Layer 1")
|
||||||
fmt.Println(lyr)
|
fmt.Println(lyr)
|
||||||
set = d.LayerSet("Indicators").LayerSet("Life")
|
// set = d.LayerSet("Indicators").LayerSet("Life")
|
||||||
fmt.Println(set)
|
// fmt.Println(set)
|
||||||
for _, lyr := range set.ArtLayers() {
|
for _, lyr := range set.ArtLayers() {
|
||||||
fmt.Println(lyr.name)
|
fmt.Println(lyr.name)
|
||||||
}
|
}
|
||||||
@@ -220,7 +222,7 @@ func TestDocumentLayerSet(t *testing.T) {
|
|||||||
|
|
||||||
func TestLoadedDoc(t *testing.T) {
|
func TestLoadedDoc(t *testing.T) {
|
||||||
var d *Document
|
var d *Document
|
||||||
byt, err := ioutil.ReadFile("Document.txt")
|
byt, err := ioutil.ReadFile("./data/Test.psd.txt")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -239,12 +241,23 @@ func TestLoadedDoc(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestJSLayer(t *testing.T) {
|
||||||
|
d, _ := ActiveDocument()
|
||||||
|
set := d.LayerSet("Group 1")
|
||||||
|
lyr := set.ArtLayer("Layer 1")
|
||||||
|
fmt.Println(JSLayer(set.Path()))
|
||||||
|
fmt.Println(JSLayer(lyr.Path()))
|
||||||
|
}
|
||||||
func TestDoJs_HideLayer(t *testing.T) {
|
func TestDoJs_HideLayer(t *testing.T) {
|
||||||
err := Open("F:\\GitLab\\dreamkeepers-psd\\Template009.1.psd")
|
err := Open(testDoc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
lyr, err := NewLayerSet("Areas/TitleBackground/", nil)
|
d, err := ActiveDocument()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
lyr, err := NewLayerSet("Group 1/", d)
|
||||||
lyr.SetVisible(false)
|
lyr.SetVisible(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -252,7 +265,7 @@ func TestDoJs_HideLayer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTextItem(t *testing.T) {
|
func TestTextItem(t *testing.T) {
|
||||||
// err := Open("F:\\GitLab\\dreamkeepers-psd\\Template009.1.psd")
|
// err := Open(testDoc)
|
||||||
// if err != nil {
|
// if err != nil {
|
||||||
// t.Fatal(err)
|
// t.Fatal(err)
|
||||||
// }
|
// }
|
||||||
@@ -305,14 +318,14 @@ func BenchmarkHideLayer(b *testing.B) {
|
|||||||
// 59ns
|
// 59ns
|
||||||
func BenchmarkHelloWorld_go(b *testing.B) {
|
func BenchmarkHelloWorld_go(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
fmt.Sprintf("Hello, world!")
|
_ = fmt.Sprintf("Hello, world!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ~35200000ns (.0352s)
|
// ~35200000ns (.0352s)
|
||||||
func BenchmarkHelloWorld_vbs(b *testing.B) {
|
func BenchmarkHelloWorld_vbs(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_, err := run("helloworld")
|
_, err := runner.Run("helloworld")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -322,7 +335,7 @@ func BenchmarkHelloWorld_vbs(b *testing.B) {
|
|||||||
// ~51700000 (0.0517)
|
// ~51700000 (0.0517)
|
||||||
func BenchmarkHelloWorld_js(b *testing.B) {
|
func BenchmarkHelloWorld_js(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_, err := DoJs("test.jsx", "Hello, World!")
|
_, err := DoJS("test.jsx", "Hello, World!")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
runner/debug.test
Normal file
BIN
runner/debug.test
Normal file
Binary file not shown.
65
runner/runner.go
Normal file
65
runner/runner.go
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
package runner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Windows is the runner for Visual Basic Scripts.
|
||||||
|
var Windows = Runner{
|
||||||
|
Cmd: "cscript.exe",
|
||||||
|
Args: []string{"/nologo"},
|
||||||
|
Ext: ".vbs",
|
||||||
|
}
|
||||||
|
|
||||||
|
// pkgpath is the path to this package.
|
||||||
|
var pkgpath string
|
||||||
|
|
||||||
|
// std is the Standard Runner.
|
||||||
|
var std Runner
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
_, file, _, _ := runtime.Caller(0)
|
||||||
|
pkgpath = filepath.Dir(file)
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "windows":
|
||||||
|
std = Windows
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Runner runs script files to communicate between the OS/Photoshop and the Go code.
|
||||||
|
type Runner struct {
|
||||||
|
Cmd string // The name of the command to run
|
||||||
|
Args []string // The arguments to pass to the command.
|
||||||
|
Ext string // The file extension to use for these commands.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run runs the standard runner with the given values.
|
||||||
|
func Run(name string, args ...string) ([]byte, error) {
|
||||||
|
var out, errs bytes.Buffer
|
||||||
|
cmd := exec.Command(std.Cmd, parseArgs(name, args...)...)
|
||||||
|
cmd.Stdout, cmd.Stderr = &out, &errs
|
||||||
|
if err := cmd.Run(); err != nil || len(errs.Bytes()) != 0 {
|
||||||
|
return out.Bytes(), errors.New(errs.String())
|
||||||
|
}
|
||||||
|
return out.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseArgs parses the given args into the correct syntax.
|
||||||
|
func parseArgs(name string, args ...string) []string {
|
||||||
|
if !strings.HasSuffix(name, std.Ext) {
|
||||||
|
name += std.Ext
|
||||||
|
}
|
||||||
|
newArgs := append(std.Args, filepath.Join(pkgpath, "scripts", name))
|
||||||
|
if strings.Contains(name, "dojs") {
|
||||||
|
newArgs = append(newArgs, args[0], fmt.Sprint(strings.Join(args[1:], ",,")))
|
||||||
|
} else {
|
||||||
|
newArgs = append(newArgs, args...)
|
||||||
|
}
|
||||||
|
return newArgs
|
||||||
|
}
|
||||||
55
runner/runner_test.go
Normal file
55
runner/runner_test.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package runner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var scripts string
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
var ok bool
|
||||||
|
_, file, _, ok := runtime.Caller(0)
|
||||||
|
if !ok {
|
||||||
|
log.Panic("package path not found")
|
||||||
|
}
|
||||||
|
pkgpath = filepath.Dir(file)
|
||||||
|
scripts = filepath.Join(pkgpath, "scripts")
|
||||||
|
}
|
||||||
|
func TestRun(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
want []byte
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{"dojs", []string{filepath.Join(scripts, "test.jsx"), filepath.Join(scripts, "test.txt"), "arg1", "arg2"},
|
||||||
|
[]byte(filepath.Join(scripts, "test.txt") + "\r\narg1\r\narg2\r\n"), false},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
_, err := Run(tt.name, tt.args...)
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("Run() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
f, err := os.Open(tt.args[1])
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
got, err := ioutil.ReadAll(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if string(got) != string(tt.want) {
|
||||||
|
t.Errorf("Run() = \n%v, want \n%v", string(got), string(tt.want))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
6
runner/scripts/getLayer.jsx
Normal file
6
runner/scripts/getLayer.jsx
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#include lib.js
|
||||||
|
app.displayDialogs=DialogModes.NO
|
||||||
|
var stdout = newFile(arguments[0]);
|
||||||
|
var lyr = eval(arguments[1]);
|
||||||
|
var lyrs = [lyr];
|
||||||
|
stdout.writeln(layers(lyrs))
|
||||||
@@ -6,22 +6,6 @@ stdout.flush();
|
|||||||
var str = layers(set.artLayers);
|
var str = layers(set.artLayers);
|
||||||
str = str.replace(/\r/g, "\\r");
|
str = str.replace(/\r/g, "\\r");
|
||||||
stdout.writeln(str);
|
stdout.writeln(str);
|
||||||
/*
|
|
||||||
for (var i = 0; i < set.artLayers.length; i++) {
|
|
||||||
var lyr = set.artLayers[i];
|
|
||||||
stdout.write(('{"Name":"' + lyr.name + '", "Bounds": [[' + lyr.bounds[0] + ',' +
|
|
||||||
lyr.bounds[1] + '],[' + lyr.bounds[2] + ',' +
|
|
||||||
lyr.bounds[3] + ']], "Visible": ' + lyr.visible + ',"Text":').replace(/ px/g, ""));
|
|
||||||
if (lyr.kind == LayerKind.TEXT)
|
|
||||||
stdout.write('"'+lyr.textItem.contents.replace(/\r/g, "\\r")+'"');
|
|
||||||
else
|
|
||||||
stdout.write("null");
|
|
||||||
stdout.write("}")
|
|
||||||
if (i != set.artLayers.length - 1)
|
|
||||||
stdout.writeln(",");
|
|
||||||
stdout.flush();
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
stdout.writeln("]");
|
stdout.writeln("]");
|
||||||
stdout.write(', "LayerSets": [')
|
stdout.write(', "LayerSets": [')
|
||||||
for (var i = 0; i < set.layerSets.length; i++) {
|
for (var i = 0; i < set.layerSets.length; i++) {
|
||||||
6
runner/scripts/test.jsx
Normal file
6
runner/scripts/test.jsx
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#include lib.js
|
||||||
|
var f = newFile(arguments[0]);
|
||||||
|
for (var i = 0; i < arguments.length; i++) {
|
||||||
|
f.writeln(arguments[i]);
|
||||||
|
}
|
||||||
|
f.close();
|
||||||
3
runner/scripts/test.txt
Normal file
3
runner/scripts/test.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
C:\Users\Spencer\go\src\github.com\sbrow\ps\runner\scripts\test.txt
|
||||||
|
arg1
|
||||||
|
arg2
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
#include lib.js
|
|
||||||
app.displayDialogs=DialogModes.NO
|
|
||||||
var stdout = newFile(arguments[0]);
|
|
||||||
var lyr = eval(arguments[1]);
|
|
||||||
var lyrs = [lyr];
|
|
||||||
stdout.writeln(layers(lyrs))
|
|
||||||
/*
|
|
||||||
stdout.write(('{"Name":"' + lyr.name + '","Bounds":[[' + lyr.bounds[0] + ',' +
|
|
||||||
lyr.bounds[1] + '],[' + lyr.bounds[2] + ',' +
|
|
||||||
lyr.bounds[3] + ']],"Visible":' + lyr.visible+',"Text":').replace(/ px/g, ""));
|
|
||||||
if (lyr.kind == LayerKind.TEXT) {
|
|
||||||
stdout.write('"'+lyr.textItem.contents.replace(/\r/g, "\\r")+'"');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
stdout.write(null)
|
|
||||||
stdout.writeln('}')
|
|
||||||
stdout.close();
|
|
||||||
*/
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
#include lib.js
|
|
||||||
|
|
||||||
// var saveFile = File(arguments[0]);
|
|
||||||
var arg = "app.activeDocument.layerSets.getByName('Text').artLayers.getByName('short').textItem.contents='When another sandman is discarded from a lane,';";
|
|
||||||
var set = eval(arg);
|
|
||||||
// set.textItem.size=10;
|
|
||||||
// var doc=app.activeDocument
|
|
||||||
// doc.layerSets.getByName("ResolveGem").merge();
|
|
||||||
// alert(doc.artLayers.getByName("ResolveGem").bounds);
|
|
||||||
// doc.activeHistoryState=doc.historyStates[doc.historyStates.length-2]
|
|
||||||
// setFormatting(0,6, "Arial", "Bold");
|
|
||||||
// saveFile.close();
|
|
||||||
46
textlayer.go
46
textlayer.go
@@ -7,26 +7,30 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TextItem holds the text element of a TextLayer.
|
||||||
type TextItem struct {
|
type TextItem struct {
|
||||||
contents string
|
contents string
|
||||||
size float64
|
size float64
|
||||||
// color Color
|
font string
|
||||||
font string
|
parent *ArtLayer
|
||||||
parent *ArtLayer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TextItemJSON is the exported version of TextItem
|
||||||
|
// that allows it to be marshaled and unmarshaled
|
||||||
|
// into JSON.
|
||||||
type TextItemJSON struct {
|
type TextItemJSON struct {
|
||||||
Contents string
|
Contents string
|
||||||
Size float64
|
Size float64
|
||||||
// Color [3]int
|
Font string
|
||||||
Font string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TextItem) Contents() string {
|
// Contents returns the raw text of the TextItem.
|
||||||
|
func (t TextItem) Contents() string {
|
||||||
return t.contents
|
return t.contents
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TextItem) Size() float64 {
|
// Size returns the font size of the TextItem
|
||||||
|
func (t TextItem) Size() float64 {
|
||||||
return t.size
|
return t.size
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,34 +40,38 @@ func (t *TextItem) MarshalJSON() ([]byte, error) {
|
|||||||
return json.Marshal(&TextItemJSON{
|
return json.Marshal(&TextItemJSON{
|
||||||
Contents: t.contents,
|
Contents: t.contents,
|
||||||
Size: t.size,
|
Size: t.size,
|
||||||
// Color: t.color.RGB(),
|
Font: t.font,
|
||||||
Font: t.font,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TextItem) UnmarshalJSON(b []byte) error {
|
// UnmarshalJSON loads the JSON data into the TextItem
|
||||||
|
func (t *TextItem) UnmarshalJSON(data []byte) error {
|
||||||
tmp := &TextItemJSON{}
|
tmp := &TextItemJSON{}
|
||||||
if err := json.Unmarshal(b, &tmp); err != nil {
|
if err := json.Unmarshal(data, &tmp); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
t.contents = tmp.Contents
|
t.contents = tmp.Contents
|
||||||
t.size = tmp.Size
|
t.size = tmp.Size
|
||||||
// t.color = RGB{tmp.Color[0], tmp.Color[1], tmp.Color[2]}
|
|
||||||
t.font = tmp.Font
|
t.font = tmp.Font
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetText sets the text to the given string.
|
||||||
func (t *TextItem) SetText(txt string) {
|
func (t *TextItem) SetText(txt string) {
|
||||||
if txt == t.contents {
|
if txt == t.contents {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
var err error
|
||||||
lyr := strings.TrimRight(JSLayer(t.parent.Path()), ";")
|
lyr := strings.TrimRight(JSLayer(t.parent.Path()), ";")
|
||||||
bndtext := "[[' + lyr.bounds[0] + ',' + lyr.bounds[1] + '],[' + lyr.bounds[2] + ',' + lyr.bounds[3] + ']]"
|
bndtext := "[[' + lyr.bounds[0] + ',' + lyr.bounds[1] + '],[' + lyr.bounds[2] + ',' + lyr.bounds[3] + ']]"
|
||||||
js := fmt.Sprintf(`%s.textItem.contents='%s';var lyr = %[1]s;stdout.writeln(('%[3]s').replace(/ px/g, ''));`,
|
js := fmt.Sprintf(`%s.textItem.contents='%s';var lyr = %[1]s;stdout.writeln(('%[3]s').replace(/ px/g, ''));`,
|
||||||
lyr, txt, bndtext)
|
lyr, txt, bndtext)
|
||||||
byt, err := DoJs("compilejs.jsx", js)
|
var byt []byte
|
||||||
|
if byt, err = DoJS("compilejs.jsx", js); err != nil {
|
||||||
|
log.Panic(err)
|
||||||
|
}
|
||||||
var bnds *[2][2]int
|
var bnds *[2][2]int
|
||||||
json.Unmarshal(byt, &bnds)
|
err = json.Unmarshal(byt, &bnds)
|
||||||
if err != nil || bnds == nil {
|
if err != nil || bnds == nil {
|
||||||
log.Println("text:", txt)
|
log.Println("text:", txt)
|
||||||
log.Println("js:", js)
|
log.Println("js:", js)
|
||||||
@@ -74,26 +82,26 @@ func (t *TextItem) SetText(txt string) {
|
|||||||
t.parent.bounds = *bnds
|
t.parent.bounds = *bnds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetSize sets the size of the TextItem's font.
|
||||||
func (t *TextItem) SetSize(s float64) {
|
func (t *TextItem) SetSize(s float64) {
|
||||||
if t.size == s {
|
if t.size == s {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
lyr := strings.TrimRight(JSLayer(t.parent.Path()), ";")
|
lyr := strings.TrimRight(JSLayer(t.parent.Path()), ";")
|
||||||
js := fmt.Sprintf("%s.textItem.size=%f;", lyr, s)
|
js := fmt.Sprintf("%s.textItem.size=%f;", lyr, s)
|
||||||
_, err := DoJs("compilejs.jsx", js)
|
_, err := DoJS("compilejs.jsx", js)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.size = s
|
t.size = s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Documentation for Format(), make to textItem
|
// Fmt applies the given font and style to all characters
|
||||||
|
// in the range [start, end].
|
||||||
func (t *TextItem) Fmt(start, end int, font, style string) {
|
func (t *TextItem) Fmt(start, end int, font, style string) {
|
||||||
var err error
|
|
||||||
if !t.parent.Visible() {
|
if !t.parent.Visible() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, err = DoJs("fmtText.jsx", fmt.Sprint(start), fmt.Sprint(end),
|
_, err := DoJS("fmtText.jsx", fmt.Sprint(start), fmt.Sprint(end), font, style)
|
||||||
font, style)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panic(err)
|
log.Panic(err)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user