From 40cde546bcfb46c3efa860a267e9bd04efd7641d Mon Sep 17 00:00:00 2001 From: Unknown Date: Thu, 26 Apr 2018 13:09:36 -0400 Subject: [PATCH 1/6] Added a TextItem Struct to Artlayers. * Converted scripts to use the same getLayers() function from lib.js --- ps_test.go | 42 +++++++++--- scripts/fmtText.jsx | 12 ++-- scripts/getActiveDoc.jsx | 21 +----- scripts/getLayer.jsx | 6 +- scripts/getLayerSet.jsx | 12 ++-- scripts/lib.js | 22 ++++++ scripts/test.jsx | 6 +- structs.go | 143 ++++++++++++++++++++++++++++----------- 8 files changed, 179 insertions(+), 85 deletions(-) diff --git a/ps_test.go b/ps_test.go index 04ba17d..720ff16 100644 --- a/ps_test.go +++ b/ps_test.go @@ -122,14 +122,14 @@ func TestLayerSet(t *testing.T) { } func TestLayer(t *testing.T) { - _, err := Layer("Border/Inner Border") + _, err := layer("Border/Inner Border") if err != nil { t.Fatal(err) } } func TestMove(t *testing.T) { - lyr, err := Layer("Group 1/Layer 1") + lyr, err := layer("Group 1/Layer 1") if err != nil { t.Fatal(err) } @@ -182,15 +182,10 @@ func TestColor(t *testing.T) { } func TestApplyDataset(t *testing.T) { - out := []byte("done!\r\n") - ret, err := ApplyDataset(" Anger") + err := ApplyDataset("Anger") if err != nil { t.Fatal(err) } - if string(ret) != string(out) { - fail := fmt.Sprintf("TestJS failed.\ngot:\t\"%s\"\nwant:\t\"%s\"", ret, out) - t.Fatal(fail) - } } func TestDocumentLayerSet(t *testing.T) { @@ -248,6 +243,37 @@ func TestDoJs_HideLayer(t *testing.T) { } } +func TestTextItem(t *testing.T) { + // err := Open("F:\\GitLab\\dreamkeepers-psd\\Template009.1.psd") + // if err != nil { + // t.Fatal(err) + // } + + d, err := ActiveDocument() + if err != nil { + t.Fatal(err) + } + for _, lyr := range d.ArtLayers() { + if lyr.Name() == "Text" { + lyr.SetText("Butts") + lyr.FmtText(0, 5, "Arial", "Regular") + lyr.FmtText(0, 3, "Arial", "Bold") + } + } + + /* byt := []byte(`{"Name": "lyr", "TextItem": {"Contents": "lyr", "Size": 12.000, "Font": "ArialItalic"}}`) + lyr := &ArtLayer{} + // byt := []byte(`{"Name": "lyr"}`) + // lyr := &TextItem{} + err := lyr.UnmarshalJSON(byt) + fmt.Printf("%+v\n", lyr) + fmt.Println(lyr.TextItem) + if err != nil { + t.Fatal(err) + } + */ +} + func BenchmarkDoc_Go(b *testing.B) { for i := 0; i < b.N; i++ { _, err := ActiveDocument() diff --git a/scripts/fmtText.jsx b/scripts/fmtText.jsx index 9b88947..1a1f408 100644 --- a/scripts/fmtText.jsx +++ b/scripts/fmtText.jsx @@ -1,12 +1,12 @@ -var start = parseInt(arguments[1]); -var end = parseInt(arguments[2]); -var fontName = arguments[3]; -var fontStyle = arguments[4]; -var colorArray = [0, 0, 0]; if(app.activeDocument.activeLayer.kind == LayerKind.TEXT){ var activeLayer = app.activeDocument.activeLayer; - var fontSize = activeLayer.textItem.size; if(activeLayer.kind == LayerKind.TEXT){ + var start = parseInt(arguments[1]); + var end = parseInt(arguments[2]); + var fontName = arguments[3]; + var fontStyle = arguments[4]; + var fontSize = activeLayer.textItem.size; + var colorArray = [0, 0, 0]; if((activeLayer.textItem.contents != "")&&(start >= 0)&&(end <= activeLayer.textItem.contents.length)){ var idsetd = app.charIDToTypeID( "setd" ); var action = new ActionDescriptor(); diff --git a/scripts/getActiveDoc.jsx b/scripts/getActiveDoc.jsx index c8d2c06..bd46c9d 100644 --- a/scripts/getActiveDoc.jsx +++ b/scripts/getActiveDoc.jsx @@ -3,25 +3,8 @@ var stdout = newFile(arguments[0]); var doc = app.activeDocument; stdout.writeln(('{"Name": "'+doc.name+'", "Height":'+doc.height+ ', "Width":'+doc.width+', "ArtLayers": [').replace(/ px/g, "")); -function layers(lyrs) { - if (typeof lyrs === 'undefined') - return; - for (var i = 0; i < lyrs.length; i++) { - var lyr = lyrs[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+'"'); - else - stdout.write("null"); - stdout.write("}") - if (i+1 != lyrs.length) - stdout.write(','); - stdout.writeln(); - } -} -layers(doc.artLayers) + +stdout.writeln(layers(doc.artLayers)) stdout.writeln('], "LayerSets": ['); function lyrSets(sets, nm) { if (typeof sets === 'undefined') diff --git a/scripts/getLayer.jsx b/scripts/getLayer.jsx index 0375de8..2e503a2 100644 --- a/scripts/getLayer.jsx +++ b/scripts/getLayer.jsx @@ -2,6 +2,9 @@ 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, "")); @@ -11,4 +14,5 @@ if (lyr.kind == LayerKind.TEXT) { else stdout.write(null) stdout.writeln('}') -stdout.close(); \ No newline at end of file +stdout.close(); +*/ \ No newline at end of file diff --git a/scripts/getLayerSet.jsx b/scripts/getLayerSet.jsx index 24117de..546a142 100644 --- a/scripts/getLayerSet.jsx +++ b/scripts/getLayerSet.jsx @@ -3,6 +3,10 @@ var stdout = newFile(arguments[0]); var set = eval(arguments[1]); stdout.writeln('{"Name": "'+set.name+'", "Visible": '+ set.visible +', "ArtLayers":['); stdout.flush(); +var str = layers(set.artLayers); +str = str.replace(/\r/g, "\\r"); +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] + ',' + @@ -17,6 +21,7 @@ for (var i = 0; i < set.artLayers.length; i++) { stdout.writeln(","); stdout.flush(); } +*/ stdout.writeln("]"); stdout.write(', "LayerSets": [') for (var i = 0; i < set.layerSets.length; i++) { @@ -27,13 +32,6 @@ for (var i = 0; i < set.layerSets.length; i++) { stdout.flush() } stdout.writeln(']') -// app.activeDocument.activeLayer=set; -// set.merge(); -// set=eval(arguments[2]); stdout.write(', "Bounds": [[],[]]'); -// stdout.write((', "Bounds": [[' + set.bounds[0] + ',' + - // set.bounds[1] + '],[' + set.bounds[2] + ',' + - // set.bounds[3] + ']]').replace(/ px/g, "")); stdout.write("}"); -// Undo(); stdout.close(); \ No newline at end of file diff --git a/scripts/lib.js b/scripts/lib.js index a409c0b..25401b7 100644 --- a/scripts/lib.js +++ b/scripts/lib.js @@ -97,4 +97,26 @@ function setFormatting(start, end, fontName, fontStyle, fontSize, colorArray) { } } } +} + +function layers(lyrs) { + if (typeof lyrs === 'undefined') + return; + var str = ""; + for (var i = 0; i < lyrs.length; i++) { + var lyr = lyrs[i]; + str += ('{"Name":"' + lyr.name + '", "Bounds": [[' + lyr.bounds[0] + ',' + + lyr.bounds[1] + '],[' + lyr.bounds[2] + ',' + + lyr.bounds[3] + ']], "Visible": ' + lyr.visible+', "TextItem": ').replace(/ px/g, ""); + if (lyr.kind == LayerKind.TEXT) { + str += ('{"Contents": "'+lyr.textItem.contents+'",').replace(/\r/g, '\\r'); + str += (' "Size": '+lyr.textItem.size+',').replace(/ pt/g, ''); + str += ' "Font": "'+lyr.textItem.font+'"}\n' + } else + str += "null"; + str += "}"; + if (i+1 != lyrs.length) + str += ','; + } + return str } \ No newline at end of file diff --git a/scripts/test.jsx b/scripts/test.jsx index 7f6b4e1..05828f3 100644 --- a/scripts/test.jsx +++ b/scripts/test.jsx @@ -1,10 +1,10 @@ #include lib.js // var saveFile = File(arguments[0]); -var arg = 'app.activeDocument.layerSets.getByName("ResolveGem");'; +var arg = 'app.activeDocument.layerSets.getByName("Text").artLayers.getByName("short");'; var set = eval(arg); -set.visible=false; -alert(set.visible) +// set.textItem.size=10; +alert(set.textItem.font) // var doc=app.activeDocument // doc.layerSets.getByName("ResolveGem").merge(); // alert(doc.artLayers.getByName("ResolveGem").bounds); diff --git a/structs.go b/structs.go index 1768b8d..a9a328c 100644 --- a/structs.go +++ b/structs.go @@ -180,16 +180,16 @@ func (d *Document) Dump() { // ArtLayer reflects certain values from an Art Layer in a Photoshop document. // -// TODO: Make TextLayer a subclass of ArtLayer. +// TODO: (2) Make TextLayer a subclass of ArtLayer. type ArtLayer struct { - name string // The layer's name. - Text *string // The contents of a text layer. - bounds [2][2]int // The layers' corners. - parent Group // The LayerSet/Document this layer is in. - visible bool // Whether or not the layer is visible. - current bool // Whether we've checked this layer since we loaded from disk. - Color // The layer's color overlay. - *Stroke // The layer's stroke. + name string // The layer's name. + bounds [2][2]int // The layers' corners. + parent Group // The LayerSet/Document this layer is in. + visible bool // Whether or not the layer is visible. + current bool // Whether we've checked this layer since we loaded from disk. + Color // The layer's color overlay. + *Stroke // The layer's stroke. + *TextItem //The layer's text, if it's a text layer. } // Bounds returns the furthest corners of the ArtLayer. @@ -207,23 +207,12 @@ type ArtLayerJSON struct { Color [3]int Stroke [3]int StrokeAmt float32 - Text *string -} - -func (a *ArtLayer) SetText(txt string) { - lyr := strings.TrimRight(JSLayer(a.Path()), ";") - js := fmt.Sprintf("%s.textItem.contents='%s';", lyr, txt) - _, err := DoJs("compilejs.jsx", js) - if err != nil { - a.Text = &txt - } - a.Refresh() + TextItem *TextItem } // MarshalJSON implements the json.Marshaler interface, allowing the ArtLayer to be // saved to disk in JSON format. func (a *ArtLayer) MarshalJSON() ([]byte, error) { - // txt := strings.Replace(*a.Text, "\r", "\\r", -1) return json.Marshal(&ArtLayerJSON{ Name: a.name, Bounds: a.bounds, @@ -231,7 +220,7 @@ func (a *ArtLayer) MarshalJSON() ([]byte, error) { Color: a.Color.RGB(), Stroke: a.Stroke.RGB(), StrokeAmt: a.Stroke.Size, - Text: a.Text, + TextItem: a.TextItem, }) } @@ -245,11 +234,11 @@ func (a *ArtLayer) UnmarshalJSON(b []byte) error { a.Color = RGB{tmp.Color[0], tmp.Color[1], tmp.Color[2]} a.Stroke = &Stroke{tmp.StrokeAmt, RGB{tmp.Stroke[0], tmp.Stroke[1], tmp.Stroke[2]}} a.visible = tmp.Visible - if tmp.Text != nil { - // s := strings.Replace(*tmp.Text, "\\r", "\r", -1) - a.Text = tmp.Text - } a.current = false + a.TextItem = tmp.TextItem + if a.TextItem != nil { + a.TextItem.parent = a + } return nil } @@ -371,18 +360,6 @@ func (a *ArtLayer) Path() string { return fmt.Sprintf("%s%s", a.parent.Path(), a.name) } -// TODO: Documentation for Format() -func (a *ArtLayer) Format(start, end int, font, style string) { - if !a.Visible() { - return - } - _, err := DoJs("fmtText.jsx", fmt.Sprint(start), fmt.Sprint(end), - font, style) - if err != nil { - log.Panic(err) - } -} - // Layer returns an ArtLayer from the active document given a specified // path string. func layer(path string) (ArtLayer, error) { @@ -419,8 +396,6 @@ func (a *ArtLayer) Visible() bool { // SetPos snaps the given layer boundry to the given point. // Valid options for bound are: "TL", "TR", "BL", "BR" -// -// TODO: Test TR and BR func (a *ArtLayer) SetPos(x, y int, bound string) { if !a.visible || (x == 0 && y == 0) { return @@ -462,7 +437,10 @@ func (a *ArtLayer) Refresh() error { tmp.SetParent(a.Parent()) a.name = tmp.name a.bounds = tmp.bounds - a.Text = tmp.Text + a.TextItem = tmp.TextItem + if a.TextItem != nil { + a.TextItem.parent = a + } a.parent = tmp.Parent() a.visible = tmp.visible a.current = true @@ -721,3 +699,86 @@ func (l *LayerSet) Refresh() { l.visible = tmp.visible l.current = true } + +type TextItem struct { + contents string + size float64 + // color Color + font string + parent *ArtLayer +} + +type TextItemJSON struct { + Contents string + Size float64 + // Color [3]int + Font string +} + +func (t *TextItem) Contents() string { + return t.contents +} + +func (t *TextItem) Size() float64 { + return t.size +} + +// MarshalJSON implements the json.Marshaler interface, allowing the TextItem to be +// saved to disk in JSON format. +func (t *TextItem) MarshalJSON() ([]byte, error) { + return json.Marshal(&TextItemJSON{ + Contents: t.contents, + Size: t.size, + // Color: t.color.RGB(), + Font: t.font, + }) +} + +func (t *TextItem) UnmarshalJSON(b []byte) error { + tmp := &TextItemJSON{} + if err := json.Unmarshal(b, &tmp); err != nil { + return err + } + t.contents = tmp.Contents + t.size = tmp.Size + // t.color = RGB{tmp.Color[0], tmp.Color[1], tmp.Color[2]} + t.font = tmp.Font + return nil +} + +func (t *TextItem) SetText(txt string) { + if txt == t.contents { + return + } + lyr := strings.TrimRight(JSLayer(t.parent.Path()), ";") + js := fmt.Sprintf("%s.textItem.contents='%s';", lyr, txt) + _, err := DoJs("compilejs.jsx", js) + if err != nil { + t.contents = txt + } +} + +func (t *TextItem) SetSize(s float64) { + if t.size == s { + return + } + lyr := strings.TrimRight(JSLayer(t.parent.Path()), ";") + js := fmt.Sprintf("%s.textItem.size=%f;", lyr, s) + _, err := DoJs("compilejs.jsx", js) + if err != nil { + t.size = s + } +} + +// TODO: Documentation for Format(), make to textItem +func (t *TextItem) Fmt(start, end int, font, style string) { + var err error + if !t.parent.Visible() { + return + } + _, err = DoJs("fmtText.jsx", fmt.Sprint(start), fmt.Sprint(end), + font, style) + if err != nil { + log.Panic(err) + } +} From c8420a46ff5dba3d90fe0dfd5eb09d198aa8b7f6 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 28 Apr 2018 15:55:04 -0400 Subject: [PATCH 2/6] gitflow-hotfix-stash: v1.0 --- colors.go | 9 ++-- examples_test.go | 10 ++++ ps.go | 129 ++++++++++++++++++++++++----------------------- ps_test.go | 32 ++++++++++++ structs.go | 36 ++++++------- 5 files changed, 132 insertions(+), 84 deletions(-) create mode 100644 examples_test.go diff --git a/colors.go b/colors.go index 0586a73..5d43d1d 100644 --- a/colors.go +++ b/colors.go @@ -14,7 +14,7 @@ type Color interface { Hex() []uint8 // The color in hexadecimal format. } -// Compare determines which of two colors is "brighter". +// Compare returns the brighter of a and b. func Compare(a, b Color) Color { A := a.RGB() B := b.RGB() @@ -26,7 +26,7 @@ func Compare(a, b Color) Color { return b } -// RGB is a color in RGB format. It implements the Color interface. +// RGB is a color format. It implements the Color interface. type RGB struct { Red int Green int @@ -38,9 +38,10 @@ 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) + src := []byte([]uint8{uint8(r.Red), uint8(r.Green), uint8(r.Blue)}) + hex := make([]byte, hex.EncodedLen(len(src))) + return []uint8(hex) } // Hex is a color in hexadecimal format. It implements the Color interface. diff --git a/examples_test.go b/examples_test.go new file mode 100644 index 0000000..4b6e479 --- /dev/null +++ b/examples_test.go @@ -0,0 +1,10 @@ +package ps + +import "fmt" + +func ExampleJSLayer() { + // The path of a layer inside a top level group. + path := "/Group 1/Layer 1" + fmt.Println(JSLayer(path)) + // Output: app.activeDocument.layerSets.getByName('Group 1').artLayers.getByName('Layer 1'); +} diff --git a/ps.go b/ps.go index 482d410..37d370f 100644 --- a/ps.go +++ b/ps.go @@ -2,6 +2,8 @@ // The interaction between the two is implemented using Javascript/VBScript. // // Currently only supports Photoshop CS5 Windows x86_64. +// +// TODO: Creatue a Photoshop struct to hold program values and functions. package ps import ( @@ -9,12 +11,12 @@ import ( "errors" "fmt" "io/ioutil" - // "log" "os" "os/exec" "path/filepath" "runtime" "strings" + // "update" ) // The name of the program that runs scripts on this OS. @@ -34,11 +36,16 @@ func init() { case "darwin": scmd = "osacript" } + // update.Update() } -// Start opens Photoshop. -func Start() error { - _, err := run("start") +// 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- you will have to implement syncing +// them yourself. +func ApplyDataset(name string) error { + _, err := DoJs("applyDataset.jsx", name) return err } @@ -48,28 +55,14 @@ func Close(save PSSaveOptions) error { return err } -// 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 -} - -// Quit exits Photoshop with the given saving option. -func Quit(save PSSaveOptions) error { - _, err := run("quit", save.String()) - return err -} - -// SaveAs saves the Photoshop document to the given location. -func SaveAs(path string) error { - _, err := run("save", path) +// DoAction runs the Photoshop Action "name" from the Action Set "set". +func DoAction(set, name string) error { + _, err := run("action", set, name) return err } // DoJs runs a Photoshop Javascript script file (.jsx) from the specified location. -// It can't directly return output, so instead the script must write their 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. func DoJs(path string, args ...string) (out []byte, err error) { // Temp file for js to output to. @@ -97,16 +90,43 @@ func DoJs(path string, args ...string) (out []byte, err error) { return cmd, err } -// Wait prints a message to the console and halts operation until the user -// signals that they are ready (by pushing enter). +// 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. // -// Useful for when you need to do something by hand in the middle of an -// otherwise automated process. (i.e. loading a dataset). -func Wait(msg string) { - fmt.Print(msg) - var input string - fmt.Scanln(&input) - fmt.Println() +// TODO: get rid of the semicolon. +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 + ";" +} + +// 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 +} + +// Quit exits Photoshop using the given save option. +func Quit(save PSSaveOptions) error { + _, err := run("quit", save.String()) + return err } // run handles running the script files, returning output, and displaying errors. @@ -143,43 +163,26 @@ func run(name string, args ...string) ([]byte, error) { return out.Bytes(), nil } -// DoAction runs the Photoshop Action "name" from the Action Set "set". -func DoAction(set, name string) error { - _, err := run("action", set, name) +// SaveAs saves the Photoshop document to the given location. +func SaveAs(path string) error { + _, err := run("save", path) 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- you will have to implement syncing -// them yourself. -func ApplyDataset(name string) error { - _, err := DoJs("applyDataset.jsx", name) +// Start opens Photoshop. +func Start() error { + _, err := run("start") return err } -// 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. +// Wait prints a message to the console and halts operation until the user +// signals that they are ready to continue (by pushing enter). // -// TODO: get rid of the semicolon. -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 + ";" +// Useful for when you need to do something by hand in the middle of an +// otherwise automated process. (i.e. importing a dataset). +func Wait(msg string) { + fmt.Print(msg) + var input string + fmt.Scanln(&input) + fmt.Println() } diff --git a/ps_test.go b/ps_test.go index 04ba17d..93d178c 100644 --- a/ps_test.go +++ b/ps_test.go @@ -1,3 +1,4 @@ +// TODO: Update package tests. package ps import ( @@ -248,6 +249,37 @@ func TestDoJs_HideLayer(t *testing.T) { } } +func TestTextItem(t *testing.T) { + // err := Open("F:\\GitLab\\dreamkeepers-psd\\Template009.1.psd") + // if err != nil { + // t.Fatal(err) + // } + + d, err := ActiveDocument() + if err != nil { + t.Fatal(err) + } + for _, lyr := range d.ArtLayers() { + if lyr.Name() == "Text" { + lyr.SetText("Butts") + // lyr.FmtText(0, 5, "Arial", "Regular") + // lyr.FmtText(0, 3, "Arial", "Bold") + } + } + + /* byt := []byte(`{"Name": "lyr", "TextItem": {"Contents": "lyr", "Size": 12.000, "Font": "ArialItalic"}}`) + lyr := &ArtLayer{} + // byt := []byte(`{"Name": "lyr"}`) + // lyr := &TextItem{} + err := lyr.UnmarshalJSON(byt) + fmt.Printf("%+v\n", lyr) + fmt.Println(lyr.TextItem) + if err != nil { + t.Fatal(err) + } + */ +} + func BenchmarkDoc_Go(b *testing.B) { for i := 0; i < b.N; i++ { _, err := ActiveDocument() diff --git a/structs.go b/structs.go index 1768b8d..82ae262 100644 --- a/structs.go +++ b/structs.go @@ -72,6 +72,10 @@ func (d *Document) Name() string { return d.name } +func (d *Document) Parent() Group { + return nil +} + // The height of the document, in pixels. func (d *Document) Height() int { return d.height @@ -100,9 +104,6 @@ func (d *Document) LayerSet(name string) *LayerSet { return nil } -func (d *Document) Parent() Group { - return nil -} func (d *Document) SetParent(g Group) {} func (d *Document) Path() string { @@ -178,21 +179,21 @@ func (d *Document) Dump() { f.Write(byt) } -// ArtLayer reflects certain values from an Art Layer in a Photoshop document. +// ArtLayer reflects some values from an Art Layer in a Photoshop document. // // TODO: Make TextLayer a subclass of ArtLayer. type ArtLayer struct { - name string // The layer's name. - Text *string // The contents of a text layer. - bounds [2][2]int // The layers' corners. - parent Group // The LayerSet/Document this layer is in. - visible bool // Whether or not the layer is visible. - current bool // Whether we've checked this layer since we loaded from disk. - Color // The layer's color overlay. - *Stroke // The layer's stroke. + name string // The layer's name. + bounds [2][2]int // The corners of the layer's bounding box. + parent Group // The LayerSet/Document this layer is in. + visible bool // Whether or not the layer is visible. + current bool // Whether we've checked this layer since we loaded from disk. + Color // The layer's color overlay effect (if any). + *Stroke // The layer's stroke effect (if any). + *TextItem // The layer's text, if it's a text layer. } -// Bounds returns the furthest corners of the ArtLayer. +// Bounds returns the coordinates of the corners of the ArtLayer's bounding box. func (a *ArtLayer) Bounds() [2][2]int { return a.bounds } @@ -257,6 +258,11 @@ func (a *ArtLayer) Name() string { return a.name } +// Parent returns the Document or LayerSet this layer is contained in. +func (a *ArtLayer) Parent() Group { + return a.parent +} + // X1 returns the layer's leftmost x value. func (a *ArtLayer) X1() int { return a.bounds[0][0] @@ -363,10 +369,6 @@ func (a *ArtLayer) SetStroke(stk Stroke, fill Color) { } } -func (a *ArtLayer) Parent() Group { - return a.parent -} - func (a *ArtLayer) Path() string { return fmt.Sprintf("%s%s", a.parent.Path(), a.name) } From 0d8be6185819d7c8acb873e921706ab8aa10dcab Mon Sep 17 00:00:00 2001 From: Unknown Date: Sun, 29 Apr 2018 21:37:58 -0400 Subject: [PATCH 3/6] Fixed an error in dojs.vbs where arguments were being split. * Fixed SetText() to update bounds. --- ps.go | 2 +- scripts/dojs.vbs | 4 +++- scripts/test.jsx | 3 +-- structs.go | 17 +++++++++++++---- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/ps.go b/ps.go index 37d370f..c39854d 100644 --- a/ps.go +++ b/ps.go @@ -148,7 +148,7 @@ func run(name string, args ...string) ([]byte, error) { if strings.Contains(name, "dojs") { args = append([]string{opts, filepath.Join(pkgpath, "scripts", name)}, args[0], - fmt.Sprintf("%s", strings.Join(args[1:], ",")), + fmt.Sprintf("%s", strings.Join(args[1:], ",,")), ) } else { args = append([]string{opts, filepath.Join(pkgpath, "scripts", name)}, args...) diff --git a/scripts/dojs.vbs b/scripts/dojs.vbs index 997320b..9e2c0b8 100644 --- a/scripts/dojs.vbs +++ b/scripts/dojs.vbs @@ -4,9 +4,11 @@ Set appRef = CreateObject("Photoshop.Application") if wScript.Arguments.Count = 0 then wScript.Echo "Missing parameters" else + ' wScript.Echo wScript.Arguments(0) + ' wScript.Echo wScript.Arguments(1) path = wScript.Arguments(0) args = wScript.Arguments(1) - error = appRef.DoJavaScriptFile(path, Split(args, ",")) + error = appRef.DoJavaScriptFile(path, Split(args, ",,")) if Not error = "true" and Not error = "[ActionDescriptor]" and Not error = "undefined" Then Err.raise 1, "dojs.vbs", error end if diff --git a/scripts/test.jsx b/scripts/test.jsx index 05828f3..c0fe37f 100644 --- a/scripts/test.jsx +++ b/scripts/test.jsx @@ -1,10 +1,9 @@ #include lib.js // var saveFile = File(arguments[0]); -var arg = 'app.activeDocument.layerSets.getByName("Text").artLayers.getByName("short");'; +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; -alert(set.textItem.font) // var doc=app.activeDocument // doc.layerSets.getByName("ResolveGem").merge(); // alert(doc.artLayers.getByName("ResolveGem").bounds); diff --git a/structs.go b/structs.go index 55f0cd0..e9f2db8 100644 --- a/structs.go +++ b/structs.go @@ -753,11 +753,20 @@ func (t *TextItem) SetText(txt string) { return } lyr := strings.TrimRight(JSLayer(t.parent.Path()), ";") - js := fmt.Sprintf("%s.textItem.contents='%s';", lyr, txt) - _, err := DoJs("compilejs.jsx", js) - if err != nil { - t.contents = txt + 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, ''));`, + lyr, txt, bndtext) + byt, err := DoJs("compilejs.jsx", js) + var bnds *[2][2]int + json.Unmarshal(byt, &bnds) + if err != nil || bnds == nil { + log.Println("text:", txt) + log.Println("js:", js) + fmt.Printf("byt: '%s'\n", string(byt)) + log.Panic(err) } + t.contents = txt + t.parent.bounds = *bnds } func (t *TextItem) SetSize(s float64) { From 311552c117113543f63b311e2fbb71394dec344f Mon Sep 17 00:00:00 2001 From: Unknown Date: Fri, 4 May 2018 14:31:15 -0400 Subject: [PATCH 4/6] Migrated colors to a subpackage --- Variables.go | 21 +++++---------------- structs.go | 25 +++++++++++++------------ 2 files changed, 18 insertions(+), 28 deletions(-) diff --git a/Variables.go b/Variables.go index 567eb48..dfbb64b 100644 --- a/Variables.go +++ b/Variables.go @@ -4,13 +4,6 @@ import ( "fmt" ) -// Colors enumerates some basic, commonly used colors. -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. // 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 @@ -42,12 +35,8 @@ 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 +const ( + PSSaveChanges PSSaveOptions = iota + 1 // Saves changes before closing documents. + PSDoNotSaveChanges // Closes documents without saving. + PSPromptToSaveChanges // Prompts whether to save before closing. +) diff --git a/structs.go b/structs.go index e9f2db8..59ee5c8 100644 --- a/structs.go +++ b/structs.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/sbrow/ps/colors" "io/ioutil" "log" "os" @@ -183,14 +184,14 @@ func (d *Document) Dump() { // // TODO: (2) Make TextLayer a subclass of ArtLayer. type ArtLayer struct { - name string // The layer's name. - bounds [2][2]int // The corners of the layer's bounding box. - parent Group // The LayerSet/Document this layer is in. - visible bool // Whether or not the layer is visible. - current bool // Whether we've checked this layer since we loaded from disk. - Color // The layer's color overlay effect (if any). - *Stroke // The layer's stroke effect (if any). - *TextItem // The layer's text, if it's a text layer. + name string // The layer's name. + bounds [2][2]int // The corners of the layer's bounding box. + parent Group // The LayerSet/Document this layer is in. + visible bool // Whether or not the layer is visible. + current bool // Whether we've checked this layer since we loaded from disk. + colors.Color // The layer's color overlay effect (if any). + *colors.Stroke // The layer's stroke effect (if any). + *TextItem // The layer's text, if it's a text layer. } // Bounds returns the coordinates of the corners of the ArtLayer's bounding box. @@ -232,8 +233,8 @@ func (a *ArtLayer) UnmarshalJSON(b []byte) error { } a.name = tmp.Name a.bounds = tmp.Bounds - a.Color = RGB{tmp.Color[0], tmp.Color[1], tmp.Color[2]} - a.Stroke = &Stroke{tmp.StrokeAmt, RGB{tmp.Stroke[0], tmp.Stroke[1], tmp.Stroke[2]}} + a.Color = colors.RGB{tmp.Color[0], tmp.Color[1], tmp.Color[2]} + a.Stroke = &colors.Stroke{tmp.StrokeAmt, colors.RGB{tmp.Stroke[0], tmp.Stroke[1], tmp.Stroke[2]}} a.visible = tmp.Visible a.current = false a.TextItem = tmp.TextItem @@ -284,7 +285,7 @@ func (a *ArtLayer) SetActive() ([]byte, error) { } // SetColor creates a color overlay for the layer -func (a *ArtLayer) SetColor(c Color) { +func (a *ArtLayer) SetColor(c colors.Color) { if a.Color.RGB() == c.RGB() { if Mode == 2 || (Mode == 0 && a.current) { // log.Println("Skipping color: already set.") @@ -318,7 +319,7 @@ func (a *ArtLayer) SetColor(c Color) { } } -func (a *ArtLayer) SetStroke(stk Stroke, fill Color) { +func (a *ArtLayer) SetStroke(stk colors.Stroke, fill colors.Color) { if stk.Size == 0 { a.Stroke = &stk a.SetColor(fill) From 0901c689b1bbc29c5eb18c6ba4fe51d7fd33c6fe Mon Sep 17 00:00:00 2001 From: Unknown Date: Fri, 4 May 2018 14:31:15 -0400 Subject: [PATCH 5/6] Migrated colors to a subpackage --- Variables.go | 21 +++++---------------- colors.go => colors/colors.go | 18 +++++++++++++----- structs.go | 25 +++++++++++++------------ 3 files changed, 31 insertions(+), 33 deletions(-) rename colors.go => colors/colors.go (89%) diff --git a/Variables.go b/Variables.go index 567eb48..dfbb64b 100644 --- a/Variables.go +++ b/Variables.go @@ -4,13 +4,6 @@ import ( "fmt" ) -// Colors enumerates some basic, commonly used colors. -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. // 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 @@ -42,12 +35,8 @@ 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 +const ( + PSSaveChanges PSSaveOptions = iota + 1 // Saves changes before closing documents. + PSDoNotSaveChanges // Closes documents without saving. + PSPromptToSaveChanges // Prompts whether to save before closing. +) diff --git a/colors.go b/colors/colors.go similarity index 89% rename from colors.go rename to colors/colors.go index 5d43d1d..acc7dd3 100644 --- a/colors.go +++ b/colors/colors.go @@ -1,10 +1,22 @@ -package ps +package colors import ( "encoding/hex" // "fmt" ) +func Black() Color { + return &RGB{0, 0, 0} +} + +func Gray() Color { + return &RGB{128, 128, 128} +} + +func White() Color { + return &RGB{255, 255, 255} +} + // Color is an interface for color objects, allowing colors to be // used in various formats. // @@ -66,7 +78,3 @@ type Stroke struct { Size float32 Color } - -// func (s *Stroke) String() string { -// return fmt.Sprintf("%vpt %v", s.Size, s.Color.RGB()) -// } diff --git a/structs.go b/structs.go index e9f2db8..59ee5c8 100644 --- a/structs.go +++ b/structs.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/sbrow/ps/colors" "io/ioutil" "log" "os" @@ -183,14 +184,14 @@ func (d *Document) Dump() { // // TODO: (2) Make TextLayer a subclass of ArtLayer. type ArtLayer struct { - name string // The layer's name. - bounds [2][2]int // The corners of the layer's bounding box. - parent Group // The LayerSet/Document this layer is in. - visible bool // Whether or not the layer is visible. - current bool // Whether we've checked this layer since we loaded from disk. - Color // The layer's color overlay effect (if any). - *Stroke // The layer's stroke effect (if any). - *TextItem // The layer's text, if it's a text layer. + name string // The layer's name. + bounds [2][2]int // The corners of the layer's bounding box. + parent Group // The LayerSet/Document this layer is in. + visible bool // Whether or not the layer is visible. + current bool // Whether we've checked this layer since we loaded from disk. + colors.Color // The layer's color overlay effect (if any). + *colors.Stroke // The layer's stroke effect (if any). + *TextItem // The layer's text, if it's a text layer. } // Bounds returns the coordinates of the corners of the ArtLayer's bounding box. @@ -232,8 +233,8 @@ func (a *ArtLayer) UnmarshalJSON(b []byte) error { } a.name = tmp.Name a.bounds = tmp.Bounds - a.Color = RGB{tmp.Color[0], tmp.Color[1], tmp.Color[2]} - a.Stroke = &Stroke{tmp.StrokeAmt, RGB{tmp.Stroke[0], tmp.Stroke[1], tmp.Stroke[2]}} + a.Color = colors.RGB{tmp.Color[0], tmp.Color[1], tmp.Color[2]} + a.Stroke = &colors.Stroke{tmp.StrokeAmt, colors.RGB{tmp.Stroke[0], tmp.Stroke[1], tmp.Stroke[2]}} a.visible = tmp.Visible a.current = false a.TextItem = tmp.TextItem @@ -284,7 +285,7 @@ func (a *ArtLayer) SetActive() ([]byte, error) { } // SetColor creates a color overlay for the layer -func (a *ArtLayer) SetColor(c Color) { +func (a *ArtLayer) SetColor(c colors.Color) { if a.Color.RGB() == c.RGB() { if Mode == 2 || (Mode == 0 && a.current) { // log.Println("Skipping color: already set.") @@ -318,7 +319,7 @@ func (a *ArtLayer) SetColor(c Color) { } } -func (a *ArtLayer) SetStroke(stk Stroke, fill Color) { +func (a *ArtLayer) SetStroke(stk colors.Stroke, fill colors.Color) { if stk.Size == 0 { a.Stroke = &stk a.SetColor(fill) From 39dea8a43032a44254d50fe360058764cf8f133c Mon Sep 17 00:00:00 2001 From: Unknown Date: Mon, 4 Jun 2018 02:10:50 -0400 Subject: [PATCH 6/6] Merge branch 'release/v1.1' --- Variables.go | 4 +--- colors/colors.go => colors.go | 23 +++++++---------------- go.mod | 1 + ps.go | 1 - sha | 1 + structs.go | 25 ++++++++++++------------- 6 files changed, 22 insertions(+), 33 deletions(-) rename colors/colors.go => colors.go (86%) create mode 100644 go.mod create mode 100644 sha diff --git a/Variables.go b/Variables.go index dfbb64b..c2ae173 100644 --- a/Variables.go +++ b/Variables.go @@ -1,8 +1,6 @@ package ps -import ( - "fmt" -) +import "fmt" // 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 diff --git a/colors/colors.go b/colors.go similarity index 86% rename from colors/colors.go rename to colors.go index acc7dd3..19f1a6a 100644 --- a/colors/colors.go +++ b/colors.go @@ -1,22 +1,13 @@ -package colors +package ps -import ( - "encoding/hex" - // "fmt" +import "encoding/hex" + +var ( + ColorBlack Color = RGB{0, 0, 0} + ColorGray Color = RGB{128, 128, 128} + ColorWhite Color = RGB{255, 255, 255} ) -func Black() Color { - return &RGB{0, 0, 0} -} - -func Gray() Color { - return &RGB{128, 128, 128} -} - -func White() Color { - return &RGB{255, 255, 255} -} - // Color is an interface for color objects, allowing colors to be // used in various formats. // diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..de251c1 --- /dev/null +++ b/go.mod @@ -0,0 +1 @@ +module github.com/sbrow/ps diff --git a/ps.go b/ps.go index c39854d..0b3db1e 100644 --- a/ps.go +++ b/ps.go @@ -16,7 +16,6 @@ import ( "path/filepath" "runtime" "strings" - // "update" ) // The name of the program that runs scripts on this OS. diff --git a/sha b/sha new file mode 100644 index 0000000..2bf85ce --- /dev/null +++ b/sha @@ -0,0 +1 @@ +cb37421ea4f7211be99ad4c5710f20155ccc8117 \ No newline at end of file diff --git a/structs.go b/structs.go index 59ee5c8..e9f2db8 100644 --- a/structs.go +++ b/structs.go @@ -5,7 +5,6 @@ import ( "encoding/json" "errors" "fmt" - "github.com/sbrow/ps/colors" "io/ioutil" "log" "os" @@ -184,14 +183,14 @@ func (d *Document) Dump() { // // TODO: (2) Make TextLayer a subclass of ArtLayer. type ArtLayer struct { - name string // The layer's name. - bounds [2][2]int // The corners of the layer's bounding box. - parent Group // The LayerSet/Document this layer is in. - visible bool // Whether or not the layer is visible. - current bool // Whether we've checked this layer since we loaded from disk. - colors.Color // The layer's color overlay effect (if any). - *colors.Stroke // The layer's stroke effect (if any). - *TextItem // The layer's text, if it's a text layer. + name string // The layer's name. + bounds [2][2]int // The corners of the layer's bounding box. + parent Group // The LayerSet/Document this layer is in. + visible bool // Whether or not the layer is visible. + current bool // Whether we've checked this layer since we loaded from disk. + Color // The layer's color overlay effect (if any). + *Stroke // The layer's stroke effect (if any). + *TextItem // The layer's text, if it's a text layer. } // Bounds returns the coordinates of the corners of the ArtLayer's bounding box. @@ -233,8 +232,8 @@ func (a *ArtLayer) UnmarshalJSON(b []byte) error { } a.name = tmp.Name a.bounds = tmp.Bounds - a.Color = colors.RGB{tmp.Color[0], tmp.Color[1], tmp.Color[2]} - a.Stroke = &colors.Stroke{tmp.StrokeAmt, colors.RGB{tmp.Stroke[0], tmp.Stroke[1], tmp.Stroke[2]}} + a.Color = RGB{tmp.Color[0], tmp.Color[1], tmp.Color[2]} + a.Stroke = &Stroke{tmp.StrokeAmt, RGB{tmp.Stroke[0], tmp.Stroke[1], tmp.Stroke[2]}} a.visible = tmp.Visible a.current = false a.TextItem = tmp.TextItem @@ -285,7 +284,7 @@ func (a *ArtLayer) SetActive() ([]byte, error) { } // SetColor creates a color overlay for the layer -func (a *ArtLayer) SetColor(c colors.Color) { +func (a *ArtLayer) SetColor(c Color) { if a.Color.RGB() == c.RGB() { if Mode == 2 || (Mode == 0 && a.current) { // log.Println("Skipping color: already set.") @@ -319,7 +318,7 @@ func (a *ArtLayer) SetColor(c colors.Color) { } } -func (a *ArtLayer) SetStroke(stk colors.Stroke, fill colors.Color) { +func (a *ArtLayer) SetStroke(stk Stroke, fill Color) { if stk.Size == 0 { a.Stroke = &stk a.SetColor(fill)