├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── Makefile ├── README.md ├── pngpaste.h └── pngpaste.m /.gitignore: -------------------------------------------------------------------------------- 1 | \#*\# 2 | *~ 3 | .DS_Store 4 | *.dSYM/ 5 | pngpaste -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # pngpaste changelog 2 | 3 | ## 0.2.3 4 | 5 | * '-b' for base64 support (thanks stef-levesque!) 6 | 7 | ## 0.2.2 8 | 9 | * Output in multiple formats, based on filename. 10 | 11 | ## 0.2.1 12 | 13 | * If file cannot be written (permission denied etc), it says so 14 | and returns a failure error code 15 | * Using '-' as filename prints to standard output (thanks betaveros!) 16 | 17 | ## 0.2.0 18 | 19 | * Saves copied PNG or PDF to clipboard (thanks joonhwan!) 20 | * Version and version flag 21 | 22 | ## Initial Release 23 | 24 | * Saves copied PNG to clipboard 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2010 Jerry Chen. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above 11 | copyright notice, this list of conditions and the following 12 | disclaimer in the documentation and/or other materials provided 13 | with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY JERRY CHEN ``AS IS'' AND ANY EXPRESS OR 16 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL JERRY CHEN OR CONTRIBUTORS BE LIABLE FOR 19 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 21 | GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 23 | IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 | OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | The views and conclusions contained in the software and documentation 28 | are those of the authors and should not be interpreted as representing 29 | official policies, either expressed or implied, of Jerry Chen. 30 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: 3 | $(CC) -Wall -g -O3 -ObjC \ 4 | -framework Foundation -framework AppKit \ 5 | -o pngpaste \ 6 | pngpaste.m 7 | install: all 8 | cp pngpaste /usr/local/bin/ 9 | clean: 10 | find . \( -name '*~' -or -name '#*#' -or -name '*.o' \ 11 | -or -name 'pngpaste' -or -name 'pngpaste.dSYM' \) \ 12 | -exec rm -rfv {} \; 13 | rm -rfv *.dSYM/ pngpaste; 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | pngpaste 2 | ======== 3 | 4 | Paste PNG into files on MacOS, much like `pbpaste` does for text. 5 | 6 | However instead of `pngpaste > thefile.png`, it's `pngpaste thefile.png`, 7 | so one does not accidentally barf binary into the console. 8 | 9 | ### Motivation 10 | 11 | [http://apple.stackexchange.com/q/11100/4795](http://apple.stackexchange.com/q/11100/4795) 12 | 13 | ### Build 14 | 15 | $ make all 16 | 17 | ### Installation 18 | 19 | From source: 20 | 21 | $ make all 22 | $ sudo make install 23 | 24 | Or with Homebrew: 25 | 26 | $ brew install pngpaste 27 | 28 | ### Usage 29 | 30 | $ pngpaste hooray.png 31 | 32 | ### Bonus and Disclaimers 33 | 34 | Supported input formats are PNG, PDF, GIF, TIF, JPEG. 35 | 36 | Supported output formats are PNG, GIF, JPEG, TIFF. 37 | 38 | Output formats are determined by the provided filename extension, 39 | falling back to PNG. 40 | 41 | It's unclear if EXIF data in JPEG sources are preserved. There's an 42 | issue with pasting into JPEG format from a GIF source. 43 | 44 | ### Error Handling 45 | 46 | Minimal :'( 47 | -------------------------------------------------------------------------------- /pngpaste.h: -------------------------------------------------------------------------------- 1 | /* 2 | * pngpaste 3 | */ 4 | 5 | #import 6 | #import 7 | #import 8 | 9 | #define APP_NAME "pngpaste" 10 | #define APP_VERSION "0.2.3" 11 | #define PDF_SCALE_FACTOR 1.5 12 | #define STDOUT_FILENAME "-" 13 | 14 | typedef enum imageType 15 | { 16 | ImageTypeNone = 0, 17 | ImageTypePDF, 18 | ImageTypeBitmap, 19 | } ImageType; 20 | 21 | typedef struct parameters 22 | { 23 | NSString *outputFile; 24 | BOOL wantsVersion; 25 | BOOL wantsUsage; 26 | BOOL wantsStdout; 27 | BOOL wantsBase64; 28 | BOOL malformed; 29 | } Parameters; 30 | 31 | void usage (); 32 | void fatal (const char *msg); 33 | void version (); 34 | 35 | ImageType extractImageType (NSImage *image); 36 | NSData *renderImageData (NSImage *image, NSBitmapImageFileType bitmapImageFileType); 37 | NSData *renderFromBitmap (NSImage *image, NSBitmapImageFileType bitmapImageFileType); 38 | NSData *renderFromPDF (NSImage *image, NSBitmapImageFileType bitmapImageFileType); 39 | NSBitmapImageFileType getBitmapImageFileTypeFromFilename (NSString *filename); 40 | NSData *getPasteboardImageData (NSBitmapImageFileType bitmapImageFileType); 41 | 42 | Parameters parseArguments (int argc, char* const argv[]); 43 | 44 | 45 | int main (int argc, char * const argv[]); 46 | -------------------------------------------------------------------------------- /pngpaste.m: -------------------------------------------------------------------------------- 1 | /* 2 | * pngpaste 3 | */ 4 | 5 | #import "pngpaste.h" 6 | 7 | void 8 | usage () 9 | { 10 | fprintf(stderr, 11 | "Usage: %s [OPTIONS] \n" 12 | "\t-\t" "Print to standard output" "\n" 13 | "\t-b\t" "Print to standard output as base64" "\n" 14 | "\t-v\t" "Version" "\n" 15 | "\t-h,-?\t" "This usage" "\n", 16 | APP_NAME); 17 | } 18 | 19 | void 20 | fatal (const char *msg) 21 | { 22 | if (msg != NULL) { 23 | fprintf(stderr, "%s: %s\n", APP_NAME, msg); 24 | } 25 | } 26 | 27 | void 28 | version () 29 | { 30 | fprintf(stderr, "%s %s\n", APP_NAME, APP_VERSION); 31 | } 32 | 33 | ImageType 34 | extractImageType (NSImage *image) 35 | { 36 | ImageType imageType = ImageTypeNone; 37 | if (image != nil) { 38 | NSArray *reps = [image representations]; 39 | NSImageRep *rep = [reps lastObject]; 40 | if ([rep isKindOfClass:[NSPDFImageRep class]]) { 41 | imageType = ImageTypePDF; 42 | } else if ([rep isKindOfClass:[NSBitmapImageRep class]]) { 43 | imageType = ImageTypeBitmap; 44 | } 45 | } 46 | return imageType; 47 | } 48 | 49 | NSData * 50 | renderImageData (NSImage *image, NSBitmapImageFileType bitmapImageFileType) 51 | { 52 | ImageType imageType = extractImageType(image); 53 | switch (imageType) { 54 | case ImageTypeBitmap: 55 | return renderFromBitmap(image, bitmapImageFileType); 56 | break; 57 | case ImageTypePDF: 58 | return renderFromPDF(image, bitmapImageFileType); 59 | break; 60 | case ImageTypeNone: 61 | default: 62 | return nil; 63 | break; 64 | } 65 | } 66 | 67 | NSData * 68 | renderFromBitmap (NSImage *image, NSBitmapImageFileType bitmapImageFileType) 69 | { 70 | return [NSBitmapImageRep representationOfImageRepsInArray:[image representations] 71 | usingType:bitmapImageFileType 72 | properties:@{}]; 73 | } 74 | 75 | NSData * 76 | renderFromPDF (NSImage *image, NSBitmapImageFileType bitmapImageFileType) 77 | { 78 | NSPDFImageRep *pdfImageRep = 79 | (NSPDFImageRep *)[[image representations] lastObject]; 80 | CGFloat factor = PDF_SCALE_FACTOR; 81 | NSRect bounds = NSMakeRect( 82 | 0, 0, 83 | pdfImageRep.bounds.size.width * factor, 84 | pdfImageRep.bounds.size.height * factor); 85 | 86 | NSImage *genImage = [[NSImage alloc] initWithSize:bounds.size]; 87 | [genImage lockFocus]; 88 | [[NSColor whiteColor] set]; 89 | NSRectFill(bounds); 90 | [pdfImageRep drawInRect:bounds]; 91 | [genImage unlockFocus]; 92 | 93 | NSData *genImageData = [genImage TIFFRepresentation]; 94 | return [[NSBitmapImageRep imageRepWithData:genImageData] 95 | representationUsingType:bitmapImageFileType 96 | properties:@{}]; 97 | } 98 | 99 | /* 100 | * Returns NSBitmapImageFileType based off of filename extension 101 | */ 102 | NSBitmapImageFileType 103 | getBitmapImageFileTypeFromFilename (NSString *filename) 104 | { 105 | static NSDictionary *lookup; 106 | if (lookup == nil) { 107 | lookup = @{ 108 | @"gif": [NSNumber numberWithInt:NSBitmapImageFileTypeGIF], 109 | @"jpeg": [NSNumber numberWithInt:NSBitmapImageFileTypeJPEG], 110 | @"jpg": [NSNumber numberWithInt:NSBitmapImageFileTypeJPEG], 111 | @"png": [NSNumber numberWithInt:NSBitmapImageFileTypePNG], 112 | @"tif": [NSNumber numberWithInt:NSBitmapImageFileTypeTIFF], 113 | @"tiff": [NSNumber numberWithInt:NSBitmapImageFileTypeTIFF], 114 | }; 115 | } 116 | NSBitmapImageFileType bitmapImageFileType = NSBitmapImageFileTypePNG; 117 | if (filename != nil) { 118 | NSArray *words = [filename componentsSeparatedByString:@"."]; 119 | NSUInteger len = [words count]; 120 | if (len > 1) { 121 | NSString *extension = (NSString *)[words objectAtIndex:(len - 1)]; 122 | NSString *lowercaseExtension = [extension lowercaseString]; 123 | NSNumber *value = lookup[lowercaseExtension]; 124 | if (value != nil) { 125 | bitmapImageFileType = [value unsignedIntegerValue]; 126 | } 127 | } 128 | } 129 | return bitmapImageFileType; 130 | } 131 | 132 | /* 133 | * Returns NSData from Pasteboard Image if available; otherwise nil 134 | */ 135 | NSData * 136 | getPasteboardImageData (NSBitmapImageFileType bitmapImageFileType) 137 | { 138 | NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard]; 139 | NSImage *image = [[NSImage alloc] initWithPasteboard:pasteBoard]; 140 | NSData *imageData = nil; 141 | 142 | if (image != nil) { 143 | imageData = renderImageData(image, bitmapImageFileType); 144 | } 145 | 146 | [image release]; 147 | return imageData; 148 | } 149 | 150 | Parameters 151 | parseArguments (int argc, char* const argv[]) 152 | { 153 | Parameters params; 154 | 155 | params.outputFile = nil; 156 | params.wantsVersion = NO; 157 | params.wantsUsage = NO; 158 | params.wantsBase64 = NO; 159 | params.wantsStdout = NO; 160 | params.malformed = NO; 161 | 162 | int ch; 163 | while ((ch = getopt(argc, argv, "bvh?")) != -1) { 164 | switch (ch) { 165 | case 'b': 166 | params.wantsBase64 = YES; 167 | return params; 168 | break; 169 | case 'v': 170 | params.wantsVersion = YES; 171 | return params; 172 | break; 173 | case 'h': 174 | case '?': 175 | params.wantsUsage = YES; 176 | return params; 177 | break; 178 | default: 179 | params.malformed = YES; 180 | return params; 181 | break; 182 | } 183 | } 184 | 185 | if (argc < 2) { 186 | params.malformed = YES; 187 | } else if (!strcmp(argv[1],STDOUT_FILENAME)) { 188 | params.wantsStdout = YES; 189 | } else { 190 | params.outputFile = 191 | [[NSString alloc] initWithCString:argv[1] 192 | encoding:NSUTF8StringEncoding]; 193 | } 194 | return params; 195 | } 196 | 197 | int 198 | main (int argc, char * const argv[]) 199 | { 200 | Parameters params = parseArguments(argc, argv); 201 | if (params.malformed) { 202 | usage(); 203 | return EXIT_FAILURE; 204 | } else if (params.wantsUsage) { 205 | usage(); 206 | return EXIT_SUCCESS; 207 | } else if (params.wantsVersion) { 208 | version(); 209 | return EXIT_SUCCESS; 210 | } 211 | 212 | NSBitmapImageFileType bitmapImageFileType = 213 | getBitmapImageFileTypeFromFilename(params.outputFile); 214 | NSData *imageData = getPasteboardImageData(bitmapImageFileType); 215 | int exitCode; 216 | 217 | if (imageData != nil) { 218 | if (params.wantsStdout) { 219 | NSFileHandle *stdout = 220 | (NSFileHandle *)[NSFileHandle fileHandleWithStandardOutput]; 221 | [stdout writeData:imageData]; 222 | exitCode = EXIT_SUCCESS; 223 | } else if (params.wantsBase64) { 224 | NSFileHandle *stdout = 225 | (NSFileHandle *)[NSFileHandle fileHandleWithStandardOutput]; 226 | NSData *base64Data = [imageData base64EncodedDataWithOptions:0]; 227 | [stdout writeData:base64Data]; 228 | exitCode = EXIT_SUCCESS; 229 | } else { 230 | if ([imageData writeToFile:params.outputFile atomically:YES]) { 231 | exitCode = EXIT_SUCCESS; 232 | } else { 233 | fatal("Could not write to file!"); 234 | exitCode = EXIT_FAILURE; 235 | } 236 | } 237 | } else { 238 | fatal("No image data found on the clipboard, or could not convert!"); 239 | exitCode = EXIT_FAILURE; 240 | } 241 | 242 | return exitCode; 243 | } 244 | --------------------------------------------------------------------------------