| 1 |
from CoreGraphics import * |
|---|
| 2 |
import sys |
|---|
| 3 |
import os |
|---|
| 4 |
import subprocess |
|---|
| 5 |
import tempfile |
|---|
| 6 |
from Carbon.File import FSPathMakeRef |
|---|
| 7 |
from Carbon.Folder import FSFindFolder |
|---|
| 8 |
from Carbon.Folders import kUserDomain, kDesktopFolderType |
|---|
| 9 |
from Carbon.CF import CFURLCreateFromFileSystemRepresentation |
|---|
| 10 |
from LaunchServices.Launch import LSSetExtensionHiddenForRef |
|---|
| 11 |
|
|---|
| 12 |
class HandledError(Exception): |
|---|
| 13 |
def __init__(self, args, message, retval): |
|---|
| 14 |
# make the arguments look distinct |
|---|
| 15 |
for index, arg in enumerate(args): |
|---|
| 16 |
if '\\' in arg: |
|---|
| 17 |
args[index] = args[index].replace('\\', '\\\\') |
|---|
| 18 |
if ' ' in arg: |
|---|
| 19 |
args[index] = '"%s"' % args[index].replace('"', '\"') |
|---|
| 20 |
description = 'An error occurred while attempting to execute the command %s' % ' '.join(args) |
|---|
| 21 |
if message: |
|---|
| 22 |
description += ':\n\n%s' % message |
|---|
| 23 |
else: |
|---|
| 24 |
description += '.\n' |
|---|
| 25 |
if retval: |
|---|
| 26 |
description += '\nThe process exited with status %d.' % retval |
|---|
| 27 |
Exception.__init__(self, description) |
|---|
| 28 |
|
|---|
| 29 |
def check(args, output, retval): |
|---|
| 30 |
if output: |
|---|
| 31 |
for index, line in enumerate(output): |
|---|
| 32 |
if line.lower().startswith('usage:'): |
|---|
| 33 |
output = output[:index] |
|---|
| 34 |
break |
|---|
| 35 |
raise HandledError(args, ''.join(output), retval) |
|---|
| 36 |
elif retval != 0: |
|---|
| 37 |
raise HandledError(args, '', retval) |
|---|
| 38 |
|
|---|
| 39 |
def execute(args): |
|---|
| 40 |
process = subprocess.Popen(args, stderr=subprocess.PIPE) |
|---|
| 41 |
retval = process.wait() |
|---|
| 42 |
check(args, process.stderr.readlines(), retval) |
|---|
| 43 |
|
|---|
| 44 |
desktopDir = FSFindFolder(kUserDomain, kDesktopFolderType, False).as_pathname() |
|---|
| 45 |
desktopContents = os.listdir(desktopDir) |
|---|
| 46 |
index = 1 |
|---|
| 47 |
outFileBase = None |
|---|
| 48 |
while outFileBase is None: |
|---|
| 49 |
outFileBase = 'Screenshot %d' % index |
|---|
| 50 |
for filePath in desktopContents: |
|---|
| 51 |
if filePath.startswith(outFileBase): |
|---|
| 52 |
index += 1 |
|---|
| 53 |
outFileBase = None |
|---|
| 54 |
break |
|---|
| 55 |
outFileBase = os.path.join(desktopDir, outFileBase) |
|---|
| 56 |
writtenPaths = [] |
|---|
| 57 |
|
|---|
| 58 |
SCREENCAPTURE_ARGS = ['/usr/sbin/screencapture', '-iW'] |
|---|
| 59 |
|
|---|
| 60 |
outFile = outFileBase + '.png' |
|---|
| 61 |
args = SCREENCAPTURE_ARGS + ['-tpng', outFile] |
|---|
| 62 |
screencapture = subprocess.Popen(args, stderr=subprocess.PIPE) |
|---|
| 63 |
retval = screencapture.wait() |
|---|
| 64 |
output = screencapture.stderr.readlines() |
|---|
| 65 |
|
|---|
| 66 |
if retval == 1 and output and 'illegal option -- t' in output[0]: |
|---|
| 67 |
# Panther: only PDF output; Finder doesn't notice new files |
|---|
| 68 |
def dumpPNG(pdf, pageNumber): |
|---|
| 69 |
if pageNumber is None: |
|---|
| 70 |
outFile = outFileBase + ".png" |
|---|
| 71 |
pageNumber = 1 |
|---|
| 72 |
else: |
|---|
| 73 |
outFile = outFileBase + "-%03d.png" % pageNumber |
|---|
| 74 |
|
|---|
| 75 |
w = pdf.getTrimBox(pageNumber).getWidth() |
|---|
| 76 |
h = pdf.getTrimBox(pageNumber).getHeight() |
|---|
| 77 |
|
|---|
| 78 |
ctx = CGBitmapContextCreateWithColor(int(w), int(h), CGColorSpaceCreateDeviceRGB(), (0,0,0,0)) |
|---|
| 79 |
ctx.drawPDFDocument(pdf.getTrimBox(pageNumber), pdf, pageNumber) |
|---|
| 80 |
ctx.writeToFile(outFile, kCGImageFormatPNG) |
|---|
| 81 |
return outFile |
|---|
| 82 |
|
|---|
| 83 |
tempDir = tempfile.mkdtemp() |
|---|
| 84 |
pdfFile = os.path.join(tempDir, 'screenshot.pdf') |
|---|
| 85 |
|
|---|
| 86 |
execute(SCREENCAPTURE_ARGS + [pdfFile]) |
|---|
| 87 |
if not os.path.exists(pdfFile): |
|---|
| 88 |
# something could have gone wrong, or user cancelled, we don't know |
|---|
| 89 |
sys.exit(0) |
|---|
| 90 |
|
|---|
| 91 |
pdf = CGPDFDocumentCreateWithProvider(CGDataProviderCreateWithFilename(pdfFile)) |
|---|
| 92 |
numberOfPages = pdf.getNumberOfPages() |
|---|
| 93 |
|
|---|
| 94 |
writtenPaths = [] |
|---|
| 95 |
if numberOfPages == 1: |
|---|
| 96 |
writtenPaths = [dumpPNG(pdf, None)] |
|---|
| 97 |
else: |
|---|
| 98 |
for pageNumber in xrange(1, numberOfPages+1): |
|---|
| 99 |
writtenPaths.append(dumpPNG(pdf, pageNumber)) |
|---|
| 100 |
|
|---|
| 101 |
os.remove(pdfFile) |
|---|
| 102 |
os.rmdir(tempDir) |
|---|
| 103 |
|
|---|
| 104 |
for path in writtenPaths: |
|---|
| 105 |
LSSetExtensionHiddenForRef(FSPathMakeRef(path)[0], True) |
|---|
| 106 |
|
|---|
| 107 |
# Carbon.File module doesn't implement FNNotify |
|---|
| 108 |
execute(['/usr/bin/osascript', '-e', 'tell app "Finder" to update desktop']) |
|---|
| 109 |
else: |
|---|
| 110 |
check(args, output, retval) |
|---|
| 111 |
if not os.path.exists(outFile): |
|---|
| 112 |
# something could have gone wrong, or user cancelled, we don't know |
|---|
| 113 |
sys.exit(0) |
|---|
| 114 |
writtenPaths = [outFile] |
|---|
| 115 |
|
|---|
| 116 |
# LaunchServices module doesn't implement LSOpenFrom*Spec |
|---|
| 117 |
execute([os.path.join(os.getenv('RESOURCEPATH'), 'launch'), '-i', 'tv.kungfoo.1001'] + writtenPaths) |
|---|