Create PDF From Document Macro (v11.0.3)

Create PDF From Document Macro (v11.0.3)

Create PDF From Document.kmmacros (11 KB)

This is the first time I have used the 'share' button in the editor, so I hope I done it right...

The macro I created and show here works well while I am watching it, but I want to be sure it will work when I am not at the keyboard... and also when I may be there but busy doing something else. So I am looking for a bit of guidance in two areas.

First, the action towards the bottom that quits the application. This could present me a situation where, if I happen to be doing some other work like say responding to an email, this action may quit the wrong application. I would want it to quit the application that it started, not some other application that might be the 'Front Application' at the time this action gets called. Also, if I happen to be otherwise using the Pages app when this action comes time to invoke, then I would not want the app to quit. I find the quit action to be necessary when several files get added to the foder at one time. Is there a way of making sure which app gets closed?

The other situation I have noticed is that sometimes... actually rarely... the entire macro doesn't run until I use a hotkey I have set to 'Cancel All Macros'. I have not been able to figure out what causes this and it is rare. The macro just doesn't do anything until I 'Cancel All Macros'. So I think it might somehow 'think' it is still working as I have the Semaphore Lock being used. I need this lock because sometimes I put several files at a time into the monitored folder and the lock makes things run a lot smoother. Is there a way to unlock the lock say after a period of time?

Also if anyone has suggestions that could make this macro run smoother and/or faster I'd really like to hear them.

Regards,

Ken

Because your macro manipulates items onscreen, it is going to be very problematic to have it running while you may be doing other things. There are just so many things to you could do while the macro is running that it would be complicated to trap for all of them.

My suggestion would be to put a controller at the start of the macro that alerts you that the macro is about to run, so you can just stop doing things until it finishes, or cancel if you're busy with something. I wrote a little macro called The Decision Maker that puts up a countdown timer box which lets you do just that—it can react differently to any number of keys you define.

I use this in a lot of macros that do GUI manipulation with a short countdown and just one option—i.e. a three second countdown and if I press Control, the macro cancels.

If it continues, you could put up a progress box with a -1 value (which just bounces the progress bar part across its range), then set it to 100 when done, so you know the macro is still running.

-rob.

1 Like

One simple way to block the mouse (it doesn't work for the keyboard) while a macro is running is to use the Display Text Large action to display a large block of text across the entire screen.

Getting the Display Text Large action to stop displaying text when the macro is finished is possible, but I'm not going to explain how to do that if the user doesn't like this partial solution.

1 Like

What causes it is that you have an instance of the macro still executing -- the semaphore is doing its intended job. What causes the macro to block is trickier, but it's most likely a dialog during either the export or quit phase.

Start by not quitting Pages after every execution -- there's no reason to, Pages sat quietly in the background uses few resources, and quitting it is obviously causing you problems.

That early "For Each" action isn't doing much -- unless that's a placeholder for some actions you want to add later, delete it so it doesn't confuse the issue. You can also remove the "Semaphore Unlock" from the end unless you are planning on adding more actions after that -- the lock is removed when the macro completes, so having that just before completion is unnecessary.

It's then a case of waiting for the next time your files stop being processed. Don't "Cancel All Macros" -- leave them running and switch to Pages to see if you can see the problem. You could also add a few "Log" actions -- for example: before you open the file; before the export; after export -- so you can look at the Engine log (~/Library/Logs/Keyboard Maestro/Engine.log) in the Console app and see where the macro has got to.

WOW... This information is great. Now I need to get to work.

My thanx to all of you for the responses.

...and yes Airy, I would like to know your method for getting the display to stop displaying. I need that in another macro and I want to try it in this macro also.

A further question I thought about during sleep last night. Is it possible to have all the actions in this macro to occur in the background so the app (Pages in this case) is not the 'front application'. Or maybe put the 'default application' into a variable so as to quit a particular application. oops, I guess that's two questions. I do plan to possibly add other types of files by extension later, maybe.

No -- you're driving the UI, so the application needs to be frontmost.

You might be able to do so if you control Pages via AppleScript, but that's a whole different ball-game and, as you extend to other file types, will require any apps involved to both be AppleScriptable and include PDF export in their dictionary.

Here's an example of exporting a pdf from Pages:

1 Like

OK... thanx Nige_S... Now I really hope you don't think this a dumb question, but...

What if I duplicate the Pages app in the applications folder with a different name then tell my macro to invoke that different name. While I do realize this might be stretching things a bit, I do so much enjoy learning from you brains by asking off-the-will questions...

Regards,

Ken

By default (and this can be changed) the Display Text Large action displays the text for five seconds. (In my example below, I change it to ONE second.) If you redisplay the same text after N seconds, the text is re-drawn, and it almost looks like it's "staying on permanently" with a "slight pulsating effect" which is WONDERFUL. So all you have to do is place the Display Text Large action in a loop so that the text is re-displayed every N seconds, but your loop has to know when to "exit."

Doing this will essentially make the mouse unusable, because the text window blocks most of the screen (as long as your text fills the screen.) And as a bonus, your text can tell the user to "wait."

Now that I think of it, it's probably best to create a macro that you can call that will do this, and you would probably end the macro by simply setting a global variable to some value. That should be very easy to do. For debugging purposes, I added a clause that lets the user make the message go away if the user moves the mouse to the upper left corner of the screen, but you can delete that once you get it working.

Depending upon the resolution of your screen, you may have to change the actual words in the message, because screen resolution impacts the size of the window. The sample text in this macro seems to work best for my screen.

1 Like

Thanx Airy... I found this very interesting. A little weird for me though... I have four monitors connected to my MacBook. Don't know if you ever got there. While the macro was running I moved the mouse to a different monitor. Sometimes the 'WARNING' followed the mouse and sometimes it didn't. It depended on which way I went and also whether or not I clicked somewhere. Regardless the warning stayed around and I had to use my escape code (shft+ctrl+optn+cmd+Q) to get out. I found that it took several iterations of the escape code to stop the macro. I also let it sit a while and it never quits on its own. Can hardly wait to put this on my partners screen... he's constantly going for coffee without locking up... ha ha

I have no idea if my macro will work for people who have multi-monitors. Sorry about that. My macro was designed for someone with a single monitor.

To what end?

You could always try it yourself, and the specifics can vary between apps, but generally:

Both apps will still be called "Pages" -- the name of the file in the Finder is not the same as the name of the app as seen by Finder, KM and its tokens, displayed in the menu bar, etc.

If neither of your Pages app is open, double-clicking on a Pages file will probably open the original Pages.

If original Pages is open, double-clicking on a Pages file will bring original Pages to the front and it will open the file.

If the copy Pages2 is open, double-clicking on a Pages file will bring Pages2 to the front and it will open the file.

If Pages is open and you try and open Pages2 the OS will either a) bring Pages to the front, or b) put up a dialog telling you Pages is already running. (There are apps that let you run multiple, independent, execution instances but it isn't "the Apple way" so I very much doubt Pages will allow it!)

KM, AppleScript, and other interactions will probably follow similar rules -- target the already-open app, regardless of whether it is the original or the copy, open the original if neither is open and it's just a general "use Pages" call, or open the specific version if neither is running and you supply the specific path to that version.

What it won't do is allow you to do macroey things in Pages2 while you're doing normal work in Pages (which I'm guessing is what you're after).

@tiffle's example AppleScript is the way to go if you want to do background PDF conversions in Pages. But if you want to expand this to other file types you'll find AS support is the same, worse, or non-existent depending on the app you are using. So you might prefer to do everything up-front and in-your-face so that the behaviour is always similar and you don't have times you can use your Mac and times you can't.

If it bothers you and the conversion isn't time critical, consider not using a folder trigger. Let the files accumulate and then batch process them when you take a well-earned break.

Thanx, Nige_S, I'm sure you are right and I have given up on that. Still I do like your in-depth response... Makes perfect sense.

Please let me trouble you with something more. Since I wrote this macro, and several others, I come to recognize that KM and for that matter Applescript quite often way out pace the computer. I have long ago learned to pepper my Applescripts with like:

repeat until (some action)
delay 0.1
end repeat

...as doing so keeps things moving yet doesn't delay any longer than necessary. Can this be used in KM macros? There is of course the repeat until action, but how difficult is it to delineate the 'some action' being predicated on the preceding action? Or is there a real great way to determine just how long a pause command need be?

I've messed with this a lot, but I would cherish your thoughts... and, of course, anybody's, as I am sure I am not the only person who has to adjust, and adjust, and adjust...

Yes, I use this all the time to solve that problem:

image

You can add dozens of possible "conditions" at the bottom of that action. For example, I do the following a lot: (and it works amazingly well)

image

If your eyes are able to determine how long a pause should be, then you can get KM to do the same thing that your eyes can do. All you have to do is tell us how your eyes are able to determine when the pause should complete. Keep in mind that you can also use your eyes to "determine completion" using other things like shell commands, and that could work like this:

image

The above example will "wait" until "MyFavoriteApp" is no longer running. Of course, KM has a condition to test "app termination" directly, but using "ps" manually has many options that KM does not have.

Now that I think about it, this action ("Execute Until") is probably the most common action I use in my macros. That's because I never TRUST an action to have completed until I can TEST that the action has actually completed. So, for example, I never use the simple "PAUSE" action because I'm always checking that the reason for the PAUSE is addressed. And if the condition is NOT addressed after a few minutes, I often have code that DETECTS that the condition was not met, and then I go handle that case, and come back later. Doing these things makes my macros extremely robust. I can let my macros run for 8 hours at a time and they never need my attention, because they handle so many error cases on their own.

Technically, for the majority of commands -- not true! Most AS and KM commands wait for a "Yep -- done that!" response before a command completes and the script/macro moves on to the next.

Practically -- you're right. But that's because what we want to wait for is the result of what the app does after the command. Opening a web page is a good example -- making a new window, requesting the URL, getting the server response can all happen in less than a second and the browser will let the script/macro know that the action is complete and the program moves on. But now the web browser has to do a load more stuff -- running JavaScripts, downloading images, render (and re-rendering!) the page -- before that button we want to click finally appears... By which time we've already failed to press the button.

The quick-and-dirty fix is to bang in some hard-coded "Pause for n seconds" actions (or delays in AS). The problem is that if you make those pauses long enough for every eventuality your macro can be v-e-r-y s-l-o-w, but if you pare them down for "normal" executions they error when things take longer than expected.

The better way is to "pause until..." a condition applies. That's effectively what you are doing in your AS snippet, and KM has an action for the same: the "Pause Until".

image

The good news is that there are a whole load of Conditions available -- which makes it way easier to use than the AS equivalent :wink:

@Airy has his own preferences, but I generally use a "Pause Until" over an "Execute Until" -- if only because the name of the action makes its intent very obvious. But "Execute Until" is great if you want more control over how often the check is made because you can include a normal "Pause" in the loop.

Sure, but Apple Text Recognition does not work with "Pause Until", only "Execute Until" using the method I showed. (This bug will be fixed in a future version of KM, according to Peter.) And Execute Until has other things it can do that Pause Until cannot, like the "Last Action Result" condition doesn't have any useful meaning in "Pause Until" but works great in "Execute Until."

Interesting. I don't use OCR anywhere near as much as you (i.e. almost never!) so I'm probably missing a nuance, but a quick test shows

image

...to work just fine.

Yes, I should have made it clear that check-timing control was just one example of why of why you might want to use "Excuse Until" rather than "Pause Until".

But "Pause Until" and "Execute Until" are conceptually different -- "stop the macro until..." versus "keep doing these actions until...". And while you can emulate a "Pause" with an "Execute" that does nothing each loop, my personal preference is to use the action that matches my "inner flow chart" for the macro.

I'm afraid to tell you that your test was flawed. You didn't actually test if that action was using Tesseract OCR or Apple Text OCR. You assumed it was using Apple Text OCR because the action says so. It turns out that the way you are doing it is actually using Tesseract OCR. This is a bug, which Peter said he would fix.

The reason I noticed it is because I was using OCR so much, I actually concluded that the OCR condition you are using was actually using Tesseract OCR instead of Apple OCR, even though the editor says it's using Apple OCR.

Ah -- so, more specifically, "the 'Pause Until' action generally works fine with OCR, but be aware that there's a bug which means that even when you select 'Apple Text Recognition' it is still using the Tesseract Engine".

I would hazard that, for the majority of people the majority of the time, that doesn't matter and "Pause Until" is doing what they need. Those who, like you, have more exacting requirements may need to use an "Execute Until" loop instead.

Yes. It will be fixed soon. I think Peter said, "Fixed for next release." I'm sorry that I can't find the link to the thread.

The Pause Until action has one advantage, it has a "Reduce CPU usage" flag. However it's pretty easy to manually add that functionality, and more, to the "Execute Until" action. In fact, now that I make that statement, maybe I should publish a macro that does just that, giving the user more control over the pausing durations.