Skip to content

Commit

Permalink
Merge branch 'release/v1.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
Spencer Brower committed Apr 21, 2018
2 parents 645c6ef + cb62b35 commit 1c57642
Show file tree
Hide file tree
Showing 33 changed files with 1,781 additions and 69 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

data/
44 changes: 44 additions & 0 deletions Variables.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package ps

import (
"fmt"
)

var Colors map[string]Color = map[string]Color{
"Black": &RGB{0, 0, 0},
"Gray": &RGB{128, 128, 128},
"White": &RGB{255, 255, 255},
}

// ModeEnum determines how aggressively the package will attempt to sync with Photoshop.
type ModeEnum int

// Holds the current mode.
var Mode ModeEnum

// Fast mode never checks layers before returning.
const Fast ModeEnum = 2

// Normal Mode Always checks to see if layers are up to date
// before returning them.
const Normal ModeEnum = 0

// Safe Mode Always loads the document from scratch. (Very Slow)
const Safe ModeEnum = 1

// PSSaveOptions is an enum for options when closing a document.
type PSSaveOptions int

func (p *PSSaveOptions) String() string {
return fmt.Sprint("", *p)
}

// PSSaveChanges saves changes before closing documents.
const PSSaveChanges PSSaveOptions = 1

// PSDoNotSaveChanges closes documents without saving.
const PSDoNotSaveChanges PSSaveOptions = 2

// PSPromptToSaveChanges prompts the user whether to save each
// document before closing it.
const PSPromptToSaveChanges PSSaveOptions = 3
71 changes: 71 additions & 0 deletions colors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package ps

import (
"encoding/hex"
// "fmt"
)

// Color is an interface for color objects, allowing colors to be
// used in various formats.
//
// RGB is the default format for everything.
type Color interface {
RGB() [3]int // The color in RGB format.
Hex() []uint8 // The color in hexadecimal format.
}

// Compare determines which of two colors is "brighter".
func Compare(a, b Color) Color {
A := a.RGB()
B := b.RGB()
Aavg := (A[0] + A[1] + A[2]) / 3
Bavg := (B[0] + B[1] + B[2]) / 3
if Aavg > Bavg {
return a
}
return b
}

// RGB is a color in RGB format. It fulfills the Color interface.
type RGB struct {
Red int
Green int
Blue int
}

// RGB returns the color in RGB format.
func (r RGB) RGB() [3]int {
return [3]int{r.Red, r.Green, r.Blue}
}

// TODO: Implement RGB.Hex()
func (r RGB) Hex() []uint8 {
return make([]uint8, 6)
}

// Hex is a color in hexadecimal format. It fulfills the Color interface.
type Hex []uint8

func (h Hex) RGB() [3]int {
src := []byte(h)
dst := make([]byte, hex.DecodedLen(len(src)))
_, err := hex.Decode(dst, src)
if err != nil {
panic(err)
}
return [3]int{int(dst[0]), int(dst[1]), int(dst[2])}
}

func (h Hex) Hex() []uint8 {
return h
}

// Stroke represents a layer stroke effect.
type Stroke struct {
Size float32
Color
}

// func (s *Stroke) String() string {
// return fmt.Sprintf("%vpt %v", s.Size, s.Color.RGB())
// }
153 changes: 132 additions & 21 deletions ps.go
Original file line number Diff line number Diff line change
@@ -1,70 +1,181 @@
// Package ps is a rudimentary API between Adobe Photoshop CS5 and Golang.
//
// Most of the interaction between the two is implemented via
// Javascript and/or VBS/Applescript.
//
// Currently only works with CS5 on Windows.
package ps

import (
"bytes"
"errors"
"fmt"
"io/ioutil"
// "log"
"os"
"os/exec"
"path"
"path/filepath"
"runtime"
"strings"
)

const (
Cmd = "cscript.exe"
Opts = "/nologo"
)
var Cmd string
var Opts string
var pkgpath string

var PKGPATH = path.Join(os.Getenv("GOPATH"), "src", "github.com", "sbrow", "ps")
func init() {
_, file, _, _ := runtime.Caller(0)
pkgpath = filepath.Dir(file)
switch runtime.GOOS {
case "windows":
Cmd = "cscript.exe"
Opts = "/nologo"
case "darwin":
Cmd = "osacript"
}
}

// Start opens Photoshop.
func Start() error {
_, err := run("start")
return err
}

func Open(path string) ([]byte, error) {
return run("open", path)
// Close closes the active document in Photoshop.
func Close(save PSSaveOptions) error {
_, err := run("close", save.String())
return err
}

func Close() error {
_, err := run("close")
// Open opens a Photoshop document with the specified path.
// If Photoshop is not currently running, it is started before
// opening the document
func Open(path string) error {
_, err := run("open", path)
return err
}

func Quit() ([]byte, error) {
return run("quit")
// Quit exits Photoshop with the given saving option.
func Quit(save PSSaveOptions) error {
_, err := run("quit", save.String())
return err
}

func Js(args ...string) ([]byte, error) {
return run("dojs", args...)
// SaveAs saves the Photoshop document to the given location.
func SaveAs(path string) error {
_, err := run("save", path)
return err
}

// DoJs runs a Photoshop Javascript script file (.jsx) from the specified location.
// It can't directly return output, so instead the scripts write their output to
// a temporary file, whose contents is then read and returned.
func DoJs(path string, args ...string) (out []byte, err error) {
// Temp file for js to output to.
outpath := filepath.Join(os.Getenv("TEMP"), "js_out.txt")
// defer os.Remove(outpath)
if !strings.HasSuffix(path, ".jsx") {
path += ".jsx"
}

args = append([]string{outpath}, args...)

// If passed a script by name, assume it's in the default folder.
if filepath.Dir(path) == "." {
path = filepath.Join(pkgpath, "scripts", path)
}

args = append([]string{path}, args...)
cmd, err := run("dojs", args...)
if err == nil {
file, err := ioutil.ReadFile(outpath)
if err == nil {
cmd = append(cmd, file...)
}
}
return cmd, err
}

// Wait prints a message to the console and halts operation until the user
// signals that they are ready (by pushing enter).
//
// Useful for when you need to do something by hand in the middle of an
// otherwise automated process.
func Wait(msg string) {
fmt.Print(msg)
var input string
fmt.Scanln(&input)
fmt.Println()
}

// run handles running the script files, returning output, and displaying errors.
func run(name string, args ...string) ([]byte, error) {
var ext string
var dir string
var out bytes.Buffer
var stderr bytes.Buffer
var errs bytes.Buffer

switch runtime.GOOS {
case "windows":
ext = ".vbs"
dir = "win"
case "darwin":
ext = ".applescript"
dir = "mac"
}
if !strings.HasSuffix(name, ext) {
name += ext
}
args = append([]string{Opts, path.Join(PKGPATH, dir, name)}, args...)

if strings.Contains(name, "dojs") {
args = append([]string{Opts, filepath.Join(pkgpath, "scripts", name)},
args[0],
fmt.Sprintf("%s", strings.Join(args[1:], ",")),
)
} else {
args = append([]string{Opts, filepath.Join(pkgpath, "scripts", name)}, args...)
}
cmd := exec.Command(Cmd, args...)
cmd.Stdout = &out
cmd.Stderr = &stderr
cmd.Stderr = &errs
err := cmd.Run()
return out.Bytes(), err
if err != nil || len(errs.Bytes()) != 0 {
return out.Bytes(), errors.New(string(errs.Bytes()))
}
return out.Bytes(), nil
}

// DoAction runs the Photoshop action with name from set.
func DoAction(set, name string) error {
_, err := run("action", set, name)
return err
}

// ApplyDataset fills out a template file with information
// from a given dataset (csv) file. It is important to note that running this
// function will change data in the Photoshop document, but will not update
// data in the Go Document struct (if any); you will have to implement syncing
// them yourself.
func ApplyDataset(name string) ([]byte, error) {
return DoJs("applyDataset.jsx", name)
}

// JSLayer "compiles" Javascript code to get an ArtLayer with the given path.
// The output always ends with a semicolon, so if you want to access a specific
// property of the layer, you'll have to trim the output before concatenating
func JSLayer(path string, art ...bool) string {
path = strings.TrimLeft(path, "/")
pth := strings.Split(path, "/")
js := "app.activeDocument"
last := len(pth) - 1
if len(art) > 0 {
pth = pth[:len(pth)-1]
last--
}
if last > 0 {
for i := 0; i < last; i++ {
js += fmt.Sprintf(".layerSets.getByName('%s')", pth[i])
}
}
if pth[last] != "" {
js += fmt.Sprintf(".artLayers.getByName('%s')", pth[last])
}
return js + ";"
}
Loading

0 comments on commit 1c57642

Please sign in to comment.