view notes from the Notes Tool?

Anyone, especially newbies, asking for help with Photoshop Scripting and Photoshop Automation - as opposed to those contributing to discussion about an aspect of Photoshop Scripting

Moderators: Tom, Kukurykus

Patrick

view notes from the Notes Tool?

Post by Patrick »

Is there any way to read the text annotations left with the notes tool in a document via js? I looked through the manual and also skimmed through the meta data and didn't see them stored in either place. Looks like a no go but thought I would check with you guys,

Patrick
Mike Hale

view notes from the Notes Tool?

Post by Mike Hale »

annotations doesn't seem to be an exposed class. It's not in the document's descriptor so I don't think you can get to it with scriptlistner.

I checked exiftool because it will read some Photoshop tags (I use it to get the slices descriptor). However annotations is not one of the tags exiftool can read.

One day I hope to get my hands on the Photoshop file format specs. Maybe then...

I noticed that CS3 will let you import a file into the doc's annotations. But I didn't see a way to read the current annotation or even tell if the doc has one.

Mike
Patrick

view notes from the Notes Tool?

Post by Patrick »

Thats what I was afraid of - thanks for looking into it.

Patrick
Mike Hale

view notes from the Notes Tool?

Post by Mike Hale »

For what it's worth, I can find the annotation tag when I look at a psd file using a hexeditor.

I'll play around it this some and see what I can come up with.

Mike
Mike Hale

view notes from the Notes Tool?

Post by Mike Hale »

I think that I will be able to come up with something that will let you find the number of notes, who created the notes, the creation date, and note contents if it's text(I haven't even looked at audio notes)

But it will be read-only. The script will not be able to create or edit notes.

If read-only is ok let me know and I'll work out the details.

Mike
Patrick

view notes from the Notes Tool?

Post by Patrick »

Mike, that would be great. One of the companies we work with sends over files sometimes with excessive notes in it, so I was just looking to write the contents out to a csv file or something of the sort. If you ever get it going I would definitely be interested in checking it out so I could use it. Thanks a lot,

Patrick
Mike Hale

view notes from the Notes Tool?

Post by Mike Hale »

I wish I had Xbytor's skill in turning something like this into a class. But as I don't here is what I have come up with. I have tested it with several different notes and it seems to be working.

As it is now it will only extract the first note.

If the image is not a tif file you will need to save a temp version as a tif. You will also need to create a batch file for exiftool to extract the raw annotations. I'm sure you can handle that but let me know if you need help. The command line for exiftool is

Code: Select allexiftool -b -annotations imageFile > dataFile

The script below uses anno.raw as the dataFile name and looks for it in c:\temp. You can edit that to match you needs.

Edit: updated script as I worked out more details. Now reads all notes-assumes text only. MH

Code: Select all// These File functions are apapted from functions
// in Xbytor's Stream.js bb/viewtopic.php?t=369
// ET is the byte order. Use 'BE' for Mac. The default byte order is Intel.
// Function names come from Tiff file format data types.
// encoding needs to set to 'BINARY'
File.prototype.readByte = function() {//aka unsigned byte
  return this.read(1).charCodeAt(0);
};
 
File.prototype.readShort = function(ET) { //aka unsigned short, word = 2 bytes
  var self = this;
  var b1 = this.readByte();
  var b2 = this.readByte();
  if(ET == "BE"){
   return (b1 << 8) + b2;
  }else{
     return (b2 << 8) + b1;
     };
};

File.prototype.readLong = function(ET) {//aka unsigned long = 4 bytes
  var self = this;
  var s1 = self.readShort(ET);
  var s2 = self.readShort(ET);
  if(ET == "BE"){
     return (s1 << 16) + s2;
  }else{
     return (s2 << 16) + s1;
  }
};

var f = new File('/c/temp/anno.raw');
f.encoding = 'BINARY';
f.open('r');
f.seek(4,0);// skip first 4 bytes - version?
var notes = new Object();
notes.toString = function(){return 'annotations'}
notes.count = f.readLong();// Number of notes
var cnt = 1;
while(cnt <= notes.count){
   var note = new Object();
   note.number = cnt;
   note.toString = function() {return 'Note'+this.number}
   note.noteTagLength = f.readLong();
   note.type = f.read(4);//Atxt = text or Asnd = sound
   note.isNoteOpen = !!f.readByte();
   note.flag = f.readByte();//x1C
   note.flag2 = f.readShort();// x01
   note.iconBounds = [f.readLong(),f.readLong(),f.readLong(),f.readLong()];//not same order as js bounds
   note.bounds = [f.readLong(),f.readLong(),f.readLong(),f.readLong()];
   note.color = [f.readShort(),f.readShort(),f.readShort(),f.readShort(),f.readShort()];//note sure how to decode
   note.authorLength = f.readByte();
   note.author = f.read(note.authorLength);// note's author
   !!(note.authorLength % 2) ? f.seek(2,1):f.seek(3,1)// align to even offset
   note.dateLength = f.readByte();
   note.noteDate = f.read(note.dateLength);
   note.noteTotalLength = f.readLong();
   note.noteType = f.read(4);//Ctxt = text
   note.noteLength = f.readLong()-2//minus tag?
   note.noteTag = f.readShort();//may be note tag - seems to always be xFExFF
   note.contents = f.read(note.noteLength).replace(/\x00/g,'');
   notes['a'+cnt] = note;
   cnt++;
}
f.close();
alert(notes.a3.author+': '+notes.a3.contents)

It's ugly and there are parts of the annotation I skip over but it does seem to work fine.

Let me know if it helps and if you need the skipped parts or need to read more than the first note.

Mike
Patrick

view notes from the Notes Tool?

Post by Patrick »

Thanks a ton Mike, I will give this a try tonight and let you know.

Patrick
Mike Hale

view notes from the Notes Tool?

Post by Mike Hale »

Patrick,

Have you had a chance to try this out yet? I've tested it many times but I would like to make sure that it works on another computer.

Mike
Patrick

view notes from the Notes Tool?

Post by Patrick »

Mike Hale wrote:Patrick,

Have you had a chance to try this out yet? I've tested it many times but I would like to make sure that it works on another computer.

Mike

Mike, just got around to giving it a go and have it almost done. The only problem I am having is looping through the notes, since they are not in a array I am not sure how to change the # (i.e. a1, a2, a3). Im sure it is a really basic programming thing I am overlooking but I am stumped. I tried stuff like

Code: Select allnotes.a+i+.contents

but it justs gives me errors. Any ideas on that part? Anyways, here is what I have so far:

Code: Select allfunction main() {
   try {

      var docRef = app.activeDocument;
      var tif = new File("/c/ps_logs/temp.tif");
      var raw = new File("/c/ps_logs/notes.raw")
      
      // save temp tiff
      docRef.saveAs(new File("/c/ps_logs/temp.tif"), new TiffSaveOptions(), true, Extension.LOWERCASE);

      $.sleep(1000);
      
      // write bat to dump the raw data in exiftool
      var bat = new File('/c/ps_logs/dumpraw.bat');
      bat.open('w');
      bat.writeln('c:');
      bat.writeln('cd C:\\ps_logs');
      bat.writeln('exiftool -b -annotations temp.tif > notes.raw');
      bat.writeln('exit');
      bat.close();
      
      // run the batch file
      bat.execute();

      $.sleep(1000);
      
////////////////////////////////////////////////////////////////////////////////////////////////////////
// chunk Mike Hale wrote
////////////////////////////////////////////////////////////////////////////////////////////////////////      
      // These File functions are apapted from functions
      // in Xbytor's Stream.js bb/viewtopic.php?t=369
      // ET is the byte order. Use 'BE' for Mac. The default byte order is Intel.
      // Function names come from Tiff file format data types.
      // encoding needs to set to 'BINARY'
      File.prototype.readByte = function() {//aka unsigned byte
        return this.read(1).charCodeAt(0);
      };
      
      File.prototype.readShort = function(ET) { //aka unsigned short, word = 2 bytes
        var self = this;
        var b1 = this.readByte();
        var b2 = this.readByte();
        if(ET == "BE"){
         return (b1 << 8) + b2;
        }else{
           return (b2 << 8) + b1;
           };
      };

      File.prototype.readLong = function(ET) {//aka unsigned long = 4 bytes
        var self = this;
        var s1 = self.readShort(ET);
        var s2 = self.readShort(ET);
        if(ET == "BE"){
           return (s1 << 16) + s2;
        }else{
           return (s2 << 16) + s1;
        }
      };

      var f = new File('/c/ps_logs/notes.raw');
      f.encoding = 'BINARY';
      f.open('r');
      f.seek(4,0);// skip first 4 bytes - version?
      var notes = new Object();
      notes.toString = function(){return 'annotations'}
      notes.count = f.readLong();// Number of notes
      var cnt = 1;
      while(cnt <= notes.count){
         var note = new Object();
         note.number = cnt;
         note.toString = function() {return 'Note'+this.number}
         note.noteTagLength = f.readLong();
         note.type = f.read(4);//Atxt = text or Asnd = sound
         note.isNoteOpen = !!f.readByte();
         note.flag = f.readByte();//x1C
         note.flag2 = f.readShort();// x01
         note.iconBounds = [f.readLong(),f.readLong(),f.readLong(),f.readLong()];//not same order as js bounds
         note.bounds = [f.readLong(),f.readLong(),f.readLong(),f.readLong()];
         note.color = [f.readShort(),f.readShort(),f.readShort(),f.readShort(),f.readShort()];//note sure how to decode
         note.authorLength = f.readByte();
         note.author = f.read(note.authorLength);// note's author
         !!(note.authorLength % 2) ? f.seek(2,1):f.seek(3,1)// align to even offset
         note.dateLength = f.readByte();
         note.noteDate = f.read(note.dateLength);
         note.noteTotalLength = f.readLong();
         note.noteType = f.read(4);//Ctxt = text
         note.noteLength = f.readLong()-2//minus tag?
         note.noteTag = f.readShort();//may be note tag - seems to always be xFExFF
         note.contents = f.read(note.noteLength).replace(/\x00/g,'');
         notes['a'+cnt] = note;
         cnt++;
      }
      f.close();
////////////////////////////////////////////////////////////////////////////////////////////////////////
// chunk Mike Hale wrote
////////////////////////////////////////////////////////////////////////////////////////////////////////      

      // write notes out to csv
      var csv = new File('/c/ps_logs/notes.csv');
      csv.open('w');
      csv.writeln("author,note");      
   
      var test = "notes.a3.contents";
      
      // loop through all the notes
      var i = 1;
      while (i <= notes.count) {
         csv.writeln(notes.a1.author +","+notes.a1.contents);
         i++;
      };

      csv.close();
         
      // cleanup
      tif.remove();
      raw.remove();
      bat.remove();

   } catch (e) {
      // if an error, make sure it does not leave any temp files behind
      if (tif.exists) {
         tif.remove();
      };
      if (raw.exists) {
         raw.remove();
      };
      if (bat.exists) {
         bat.remove();
      };
      
      // show the error
      alert(e);
   }
}

main();

Thanks a lot for figuring out how to read the annotations, all that reading bytes is way over my head!

Patrick