I am trying to count the words in Pages document (the doc format is RTF) using the scripting bridge. (I can do it using NSApplescript but I would rather not have all the applescript-aware duct tape in my code)
When I perform this task using applescript (and NSAppleScript APIs) I can do this very simply (and successfully):
on wordCount(appName,docName)
local mydoc
local wordcount
tell application appName
set mydoc to document docName
set wordcount to count of words of mydoc
log "wordcount = " & wordcount
return wordcount
end tell
end wordCount
However when I try the equivalent using the scripting bridge all my objects seem to have null contents. My code is as follows:
+ (NSUInteger) wordCountForApp: (SBApplication*) sbApp docNamed: (NSString*) docName
{
PagesApplication *pages = (PagesApplication*)sbApp;
PagesDocument *doc = [[pages documents] objectWithName:docName];
PagesText *text = [doc bodyText];
SBElementArray *words = [text words];
NSUInteger wc = [words count];
NSLog(#"Pages word count = %ul", (unsigned int) wc);
return wc; // wc comes back as zero always ... grrrr
}
I have verified that I am running this stuff on the main thread (and that equivalent code works against TextEdit). Any ideas as to what is going on/how to work around?
Thanks for having read this far....
That code works for me. The equivalent in F-Script:
> pages := SBApplication applicationWithBundleIdentifier: 'com.apple.iWork.Pages'
> (pages documents objectWithName: 'fun') bodyText words count
303
I'd suggest you step through it with a debugger and make sure that each object is what you're expecting, i.e. pages, [pages documents], etc.
If you're writing this code for external use, ideally you should not refer to documents by name; the user may have multiple documents with the same name open.
Another option is objc-appscript, which provides an neat ASTranslate utility that transforms AppleScript into Objective-C (or Ruby or Python for the other appscript bindings). For example, for the above, you'd have something like:
#import "PGGlue/PGGlue.h"
PGApplication *pages = [PGApplication applicationWithName: #"Pages"];
PGReference *ref = [[pages documents] byName: #"fun"];
PGCountCommand *cmd = [[ref count] each: [PGConstant word]];
id result = [cmd send];
So the problem was that I was asking for a document named "something.rtf" that I had just opened in Pages. But when Pages opens "something.rtf" it names it "something". And then when you ask for document named "something.rtf" it does not return nil because the document does not exist by that name. Instead, it returns a PagesDocument named "something.rtf" which has no valid contents: a NIL document. Which I guess I just was too stupid to recognize when I submitted to SO.
I have since checked other apps and this seems to be normal applescript behavior when you ask for a document by name (to get back a "valid object" containing a NIL document) oh well.
Related
I have implement xmpp framework in my cocoa os x application. currently am working on vCard. i have done work on set all required field's data of login user and it stored successfully, but i have no proper solution for how to get login user's stored vcard and am not aware of it. Please give me solution for this problem. i have been suffering for this from last 3 days. help me as soon as
Thanx in advance
i have used below code to set vCard field
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_PRIORITY_DEFAULT);
dispatch_async(queue, ^
{
XMPPvCardCoreDataStorage *xmppvCardStorage = [XMPPvCardCoreDataStorage sharedInstance];
XMPPvCardTempModule *xmppvCardTempModule = [[XMPPvCardTempModule alloc] initWithvCardStorage:xmppvCardStorage];
[xmppvCardTempModule activate:[self xmppStream]];
XMPPvCardTemp *myvCardTemp = [xmppvCardTempModule myvCardTemp];
if (!myvCardTemp)
{
NSXMLElement *vCardXML = [NSXMLElement elementWithName:#"vCard" xmlns:#"vcard-temp"];
XMPPvCardTemp *newvCardTemp = [XMPPvCardTemp vCardTempFromElement:vCardXML];
[newvCardTemp setName:#"vCard"];
[newvCardTemp setNickname:lbl.stringValue];
[newvCardTemp setFormattedName:#"abc"];
[newvCardTemp setDesc:lbl_abt.stringValue];
[xmppvCardTempModule updateMyvCardTemp:newvCardTemp];
}
else
{
[myvCardTemp setName:#"vCard"];
[myvCardTemp setNickname:lbl.stringValue];
[myvCardTemp setFormattedName:#"abc"];
[myvCardTemp setDesc:lbl_abt.stringValue];
[xmppvCardTempModule updateMyvCardTemp:myvCardTemp];
}
});
}
And i also tried below code to retrieve vCard
/* XMPPvCardTempModule *xmppvCardTempModule;
XMPPvCardTemp *vCard =[xmppvCardTempModule vCardTempForJID:[XMPPJID jidWithString:#"xxxx"] shouldFetch:YES];
NSLog(#"V CARD :%#",vCard.nickname);*/
XMPPvCardCoreDataStorage* xmppvCardStorage = [XMPPvCardCoreDataStorage sharedInstance];
XMPPvCardTempModule* m = [[XMPPvCardTempModule alloc]initWithvCardStorage:xmppvCardStorage];
[m fetchvCardTempForJID:[XMPPJID jidWithString:#"xxxx"]ignoreStorage:YES];
NSLog(#"%#",xmppvCardStorage.description);
Please suggest me proper way to solve this problem :
I am trying to perform a copy/paste for my to the the last active app, here's my code:
NSString *appleScriptSource = [NSString stringWithFormat:#"\ntell application \"%#\" to activate\ntell application \"System Events\" to tell process \"%#\"\nkeystroke \"v\" using command down\nend tell", [lastApp localizedName], [lastApp localizedName]];
NSDictionary *error;
NSAppleScript *aScript = [[NSAppleScript alloc] initWithSource:appleScriptSource];
NSAppleEventDescriptor *aDescriptor = [aScript executeAndReturnError:&error];
The problem is that on some computers it works just fine, but on others it fails. My error output from error that is returned by executeAndReturnError is:
2012-06-13 17:43:19.875 Mini Translator[1206:303] (null) (error: {
NSAppleScriptErrorBriefMessage = "Expected end of line but found \U201c\"\U201d.";
NSAppleScriptErrorMessage = "Expected end of line but found \U201c\"\U201d.";
NSAppleScriptErrorNumber = "-2741";
NSAppleScriptErrorRange = "NSRange: {95, 1}";
})
I can't seem to figure out what it means or why it happens.
We tried copying the generated apple-script code into the Apple Script editor, and here it works just fine.
My App is sandboxed - i have added the bundle identifiers for the key "com.apple.security.temporary-exception.apple-events" for the apps i want to support.
Any suggestions?
I am guessing that the \u201c and \u201d are red herrings and just represent smart quotes around a double quote in the error message produced by apple script, and your issue lies with the localized name of the last application you are formatting into the script. I am not sure why you might see this on one machine and not another.
For example if the name was 'Some " App' then the double quotes would end up mismatched as it would end up injected into the middle of a double quoted string. You might want to try and replace any double quotes in the name with '\"' which will escape them.
e.g.
NSString *esc = [[lastApp localizedName] stringByReplacingOccurrencesOfString:#"\"" withString:#"\\\""];
I am generating a PDF file and saving it to my device before e-mailing it from within my application. At the moment it does not get cleared down either manually or automatically so I was looking at NSTemporaryDirectory. I have looked at various sites and also on here for the answer to my specific query but cannot find it.
I have the following function:
+(NSString *)getTempFilePathName {
NSString *tempFilePathName = nil;
NSString *tempFileTemplate = [NSTemporaryDirectory() stringByAppendingPathComponent:#"tempfile.XXXXXX"];
const char *tempFileTemplateCString = [tempFileTemplate fileSystemRepresentation];
char *tempFileNameCString = (char *)malloc(strlen(tempFileTemplateCString) + 1);
strcpy(tempFileNameCString, tempFileTemplateCString);
int fileDescriptor = mkstemp(tempFileNameCString);
if (fileDescriptor != -1) {
// File opened successfully
tempFilePathName = [[NSFileManager defaultManager] stringWithFileSystemRepresentation:tempFileNameCString length:strlen(tempFileNameCString)];
close(fileDescriptor);
}
free(tempFileNameCString);
return tempFilePathName;
}
This returns me an NSString with the full path and filename to a temp file ending with the 'XXXXXX' component of the filename replaced with unique combo of letters and numbers. As I am using this to save a PDF file, I need to fix the 'XXXXXX' bit with the extension ".PDF". Preferably I'd like to also specify the filename itself too so something like "Order-123.PDF".
Can I just edit my method to pass in a filename and use that in the stringByAppendingPathComponent parameter? Is the directory name generated unique per call to this method?
I have solved this by myself. After the line that calls the above routine I do:
NSString *newFilePath = [tempNameStub stringByAppendingPathExtension:#"pdf"];
Which adds .pdf to the end.
there are some similars questions here but none involves NSApplescript.
I'm trying to add iTunes artwork to a selected track, my code is:
set newFile to add aFileName as POSIX file to ipod_lib
set current_track to newFile
set jpegFilename to ":temp:artwork.jpg" as string
set data of artwork 1 of current_track to (read (file jpegFilename) as picture)
tell current_track
set name to "Song Name" as string
set artist to "Custom Artist" as string
set album to "Custom Album" as string
set year to "2011" as string
set track number to "1" as number
set track count to "38" as number
end tell
Where aFileName is the path of an mp3.
I'm testing this using Script Debugger 4.5 and it works fine, but when I copy the code to my Xcode project and run it wont set the artwork neither the other meta data.
But if I comment the "set data of artwork.." line, then it does set the other meta data (name, artist, etc)
My Xcode code is:
NSString *scriptote = #"with timeout of 600 seconds\n"
"tell application \"iTunes\"\n"
...
"set newFile to add aFileName as POSIX file to ipod_lib\n"
"set current_track to newFile\n"
"set jpegFilename to \":temp:artwork.jpg\" as string\n"
// If a comment the following line, everything else works, if I left this line, no meta data is set.
"set data of artwork 1 of current_track to (read (file jpegFilename) as picture)\n"
"tell current_track\n"
"set name to \"Song Name\" as string\n"
"set artist to \"Custom Artist\" as string\n"
"set album to \"Custom Album\" as string\n"
"set year to \"2011\" as string\n"
"set track number to \"1\" as number\n"
"set track count to \"38\" as number\n"
"end tell\n"
...
;
if ((ascript = [[NSAppleScript alloc] initWithSource:scriptote])) {
status = [[ascript executeAndReturnError:&errorInfo] stringValue];
if (!errorInfo) {
// do something
NSLog(#"NO Error");
} else {
// process errors
// NSRange errorRange = [[errorInfo objectForKey:#"NSAppleScriptErrorRange"] rangeValue];
NSLog(#"Error\n Error line: ");
}
[ascript release];
}
I have been searching on google for many hours without luck.
I don't want to use ScriptBridge framework because I will need to translate lot of applescripts so its not an option for me right now.
Any help will be greatly appreciated.
Found the problem, in case someone else reach here in the future the problem was with the HFS path format style.
I was getting the path to my artwork using NSSearchPathForDirectoriesInDomains and then replacing all the #"/" for #":" to format the path in HFS style.
The problem is that HFS path format style need a full path including Volume Name and NSSearchPathForDirectoriesInDomain and any other NSFileManager methods return paths starting in /Users/.... and I needed a path like this: Macintosh HD/Users/...
To get the HFS full path I used the following code:
// Get an HFS-style reference to a specified file
// (imagePath is an NSString * containing a POSIX-style path to the artwork image file)
NSURL *fileURL = [NSURL fileURLWithPath:imagePath];
NSString *pathFormatted = (NSString *)CFURLCopyFileSystemPath((CFURLRef)fileURL, kCFURLHFSPathStyle);
That solves the problem, use the pathFormatted string to set the data of artwork and it should work.
Hope this would help someone someday -)
I have a problem running the NativeProcess if I put spaces in the arguments
if (Capabilities.os.toLowerCase().indexOf("win") > -1)
{
fPath = "C:\\Windows\\System32\\cmd.exe";
args.push("/c");
args.push(scriptDir.resolvePath("helloworld.bat").nativePath);
}
file = new File(fPath);
var nativeProcessStartupInfo:NativeProcessStartupInfo = new NativeProcessStartupInfo();
nativeProcessStartupInfo.executable = file;
args.push("blah");
nativeProcessStartupInfo.arguments = args;
process = new NativeProcess();
process.start(nativeProcessStartupInfo);
in the above code, if I use
args.push("blah") everything works fine
if I use
args.push("blah blah") the program breaks as if the file wasn't found.
Seems like I'm not the only one:
http://tech.groups.yahoo.com/group/flexcoders/message/159521
As one of the users their pointed out, it really seems like an awful limitation by a cutting edge SDK of 21st century. Even Alex Harui didn't have the answer there and he's known to workaround every Adobe bug:)
Any ideas?
I am using AIR 2.6 SDK in JavaScript like this, and it is working fine even for spaces.
please check your code with this one.
var file = air.File.applicationDirectory;
file = file.resolvePath("apps");
if (air.Capabilities.os.toLowerCase().indexOf("win") > -1)
{
file = file.resolvePath(appFile);
}
var nativeProcessStartupInfo = new air.NativeProcessStartupInfo();
nativeProcessStartupInfo.executable = file;
var args =new air.Vector["<String>"]();
for(i=0; i<arguments.length; i++)
args.push(arguments[i]);
nativeProcessStartupInfo.arguments = args;
process = new air.NativeProcess();
process.addEventListener(air.ProgressEvent.STANDARD_OUTPUT_DATA, onOutputData);
process.addEventListener(air.ProgressEvent.STANDARD_INPUT_PROGRESS, inputProgressListener);
process.start(nativeProcessStartupInfo);
To expand on this: The reason that this works (see post above):
var args =new air.Vector["<String>"]();
for(i=0; i<arguments.length; i++)
args.push(arguments[i]);
nativeProcessStartupInfo.arguments = args;
is that air expects that the arguments being passed to the nativeProcess are delimited by spaces. It chokes if you pass "C:\folder with spaces\myfile.doc" (and BTW for AIR a file path for windows needs to be "C:\\folder with spaces\\myfile.doc") you would need to do this:
args.push("C:\\folder");
args.push("with");
args.push("spaces\\myfile.doc");
Hence, something like this works:
var processArgs = new air.Vector["<String>"]();
var path = "C:\\folder with spaces\\myfile.doc"
var args = path.split(" ")
for (var i=0; i<args.length; i++) {
processArgs.push(args[i]);
};
UPDATE - SOLUTION
The string generated by the File object by either nativePath or resolvePath uses "\" for the path. Replace "\" with "/" and it works.
I'm having the same problem trying to call 7za.exe using NativeProcess. If you try to access various windows directories the whole thing fails horribly. Even trying to run command.exe and calling a batch file fails because you still have to try to pass a path with spaces through "arguments" on the NativeProcessStartupInfo object.
I've spent the better part of a day trying to get this to work and it will not work. Whatever happens to spaces in "arguments" totally destroys the path.
Example 7za.exe from command line:
7za.exe a MyZip.7z "D:\docs\My Games\Some Game Title\Maps\The Map.map"
This works fine. Now try that with Native Process in AIR. The AIR arguments sanitizer is FUBAR.
I have tried countless ways to put in arguments and it just fails. Interesting I can get it to spit out a zip file but with no content in the zip. I figure this is due to the first argument set finally working but then failing for the path argument.
For example:
processArgs[0] = 'a';
processArgs[1] = 'D:\apps\flash builder 4.5\project1\bin-debug\MyZip.7z';
processArgs[2] = 'D:\docs\My Games\Some Game Title\Maps\The Map.map';
For some reason this spits out a zip file named: bin-debugMyZip.7z But the zip is empty.
Whatever AIR is doing it is fraking up path strings. I've tried adding quotes around those paths in various ways. Nothing works.
I thought I could fall back on calling a batch file from this example:
http://technodesk.wordpress.com/2010/04/15/air-2-0-native-process-batch-file/
But it fails as well because it still requires the path to be passed through arguments.
Anyone have any luck calling 7z or dealing with full paths in the NativeProcess? All these little happy tutorials don't deal with real windows folder structure.
Solution that works for me - set path_with_space as "nativeProcessStartupInfo.workingDirectory" property. See example below:
public function openPdf(pathToPdf:String):void
}
var nativeProcessStartupInfo:NativeProcessStartupInfo = new NativeProcessStartupInfo();
var file:File = File.applicationDirectory.resolvePath("C:\\Windows\\System32\\cmd.exe");
nativeProcessStartupInfo.executable = file;
if (Capabilities.os.toLowerCase().indexOf("win") > -1)
{
nativeProcessStartupInfo.workingDirectory = File.applicationDirectory.resolvePath(pathToPdf).parent;
var processArgs:Vector.<String> = new Vector.<String>();
processArgs[0] = "/k";
processArgs[1] = "start";
processArgs[2] = "test.pdf";
nativeProcessStartupInfo.arguments = processArgs;
process = new NativeProcess();
process.start(nativeProcessStartupInfo);
process.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, onOutputData);
}
args.push( '"blah blah"' );
Command line after all supports spaces if they are nested whithin "".
So if lets say you have a file argument :
'test/folder with space/blah'
Convert it to the following
'test/"folder with space"/blah'
Optionally use a filter:
I once had a problem like this in AIR, i just simply filter the text before i push it into the array. My refrence use CASA lib though
import org.casalib.util.ArrayUtil;
http://casalib.org/
/**
* Filters a string input for 'safe handling', and returns it
**/
public function stringFilter(inString:String, addPermitArr:Array = null, permitedArr:Array = null):String {
var sourceArr:Array = inString.split(''); //Splits the string input up
var outArr:Array = new Array();
if(permitedArr == null) {
permitedArr = ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890" as String).split('');
}
if( addPermitArr != null ) {
permitedArr = permitedArr.concat( addPermitArr );
}
for(var i:int = 0; i < sourceArr.length; i++) {
if( ArrayUtil.contains( permitedArr, sourceArr[i] ) != 0 ) { //it is allowed
outArr.push( sourceArr[i] );
}
}
return (outArr.join('') as String);
}
And just filter it via
args.push( stringFilter( 'blah blah', new Array('.') ) );
Besides, it is really bad practice to use spaces in file names / arguments, use '_' instead. This seems to be originating from linux though. (The question of spaces in file names)
This works for me on Windws7:
var Xargs:Array = String("/C#echo#a trully hacky way to do this :)#>#C:\\Users\\Benjo\\AppData\\Roaming\\com.eblagajna.eBlagajna.POS\\Local Store\\a.a").split("#");
var args:Vector.<String> = new Vector.<String>();
for (var i:int=0; i<Xargs.length; i++) {
trace("Pushing: "+Xargs[i]);
args.push(Xargs[i]);
};
NPI.arguments = args;
If your application path or parameter contains spaces, make sure to wrap it in quotes. For example path of the application has spaces C:\Program Files (x86)\Camera\Camera.exe use quotes like:
"C:\Program Files (x86)\Camera\Camera.exe"