diff --git a/Variables.go b/Variables.go index 567eb48..c2ae173 100644 --- a/Variables.go +++ b/Variables.go @@ -1,15 +1,6 @@ package ps -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}, -} +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 @@ -42,12 +33,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.go index 0586a73..19f1a6a 100644 --- a/colors.go +++ b/colors.go @@ -1,8 +1,11 @@ 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} ) // Color is an interface for color objects, allowing colors to be @@ -14,7 +17,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 +29,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 +41,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. @@ -65,7 +69,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/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/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 482d410..0b3db1e 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,7 +11,6 @@ import ( "errors" "fmt" "io/ioutil" - // "log" "os" "os/exec" "path/filepath" @@ -34,11 +35,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 +54,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,68 +89,6 @@ 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). -// -// 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() -} - -// run handles running the script files, returning output, and displaying errors. -func run(name string, args ...string) ([]byte, error) { - var ext string - var out bytes.Buffer - var 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.Sprintf("%s", 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(string(errs.Bytes())) - } - 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) - 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) - 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. @@ -183,3 +113,75 @@ func JSLayer(path string, art ...bool) string { } 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. +func run(name string, args ...string) ([]byte, error) { + var ext string + var out bytes.Buffer + var 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.Sprintf("%s", 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(string(errs.Bytes())) + } + return out.Bytes(), nil +} + +// SaveAs saves the Photoshop document to the given location. +func SaveAs(path string) error { + _, err := run("save", path) + return err +} + +// Start opens Photoshop. +func Start() error { + _, err := run("start") + return err +} + +// Wait prints a message to the console and halts operation until the user +// 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 +// 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..a6aa35a 100644 --- a/ps_test.go +++ b/ps_test.go @@ -1,3 +1,4 @@ +// TODO: Update package tests. package ps import ( @@ -122,14 +123,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 +183,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 +244,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/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/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..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("ResolveGem");'; +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.visible=false; -alert(set.visible) +// set.textItem.size=10; // var doc=app.activeDocument // doc.layerSets.getByName("ResolveGem").merge(); // alert(doc.artLayers.getByName("ResolveGem").bounds); 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 1768b8d..e9f2db8 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. +// 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 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 } @@ -207,23 +208,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 +221,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 +235,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 } @@ -257,6 +247,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,26 +358,10 @@ 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) } -// 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 +398,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 +439,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 +701,95 @@ 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()), ";") + 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) { + 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) + } +}