Basic support for Image in PDF Context + support for printing to a PDF file

This commit is contained in:
Airy ANDRE 2012-01-18 15:59:12 -08:00
parent b5ab411409
commit 235e290936
9 changed files with 141 additions and 56 deletions

View File

@ -73,7 +73,6 @@ id NSApp=nil;
+(void)initialize {
if(self==[NSApplication class]){
[NSClassFromString(@"Win32RunningCopyPipe") performSelector:@selector(startRunningCopyPipe)];
}
}

View File

@ -793,7 +793,6 @@ forSaveOperation:(NSSaveOperationType)operation
if(operation==nil){
return;
}
[operation setShowsPrintPanel:showPrintPanel];
[operation runOperation];
}

View File

@ -24,6 +24,13 @@ APPKIT_EXPORT NSString * const NSPrintPrinterName;
APPKIT_EXPORT NSString * const NSPrintJobDisposition;
APPKIT_EXPORT NSString * const NSPrintDetailedErrorReporting;
APPKIT_EXPORT NSString * const NSPrintSpoolJob;
APPKIT_EXPORT NSString * const NSPrintPreviewJob;
APPKIT_EXPORT NSString * const NSPrintSaveJob;
APPKIT_EXPORT NSString * const NSPrintCancelJob;
APPKIT_EXPORT NSString * const NSPrintSavePath;
APPKIT_EXPORT NSString * const NSPrintCopies;
APPKIT_EXPORT NSString * const NSPrintAllPages;
APPKIT_EXPORT NSString * const NSPrintFirstPage;

View File

@ -18,6 +18,13 @@ NSString * const NSPrintPrinterName=@"NSPrintPrinterName";
NSString * const NSPrintJobDisposition=@"NSPrintJobDisposition";
NSString * const NSPrintDetailedErrorReporting=@"NSPrintDetailedErrorReporting";
NSString * const NSPrintSpoolJob = @"NSPrintSpoolJob";
NSString * const NSPrintPreviewJob = @"NSPrintPreviewJob";
NSString * const NSPrintSaveJob = @"NSPrintSaveJob";
NSString * const NSPrintCancelJob = @"NSPrintCancelJob";
NSString * const NSPrintSavePath = @"NSPrintSavePath";
NSString * const NSPrintCopies=@"NSPrintCopies";
NSString * const NSPrintAllPages=@"NSPrintAllPages";
NSString * const NSPrintFirstPage=@"NSPrintFirstPage";

View File

@ -17,7 +17,8 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
enum {
NSPrintOperationPDFInRect,
NSPrintOperationEPSInRect,
NSPrintOperationPrinter,
NSPrintOperationPrinter,
NSPrintOperationFile,
};
@implementation NSPrintOperation
@ -31,7 +32,7 @@ static NSPrintOperation *_currentOperation=nil;
-initWithView:(NSView *)view printInfo:(NSPrintInfo *)printInfo insideRect:(NSRect)rect toData:(NSMutableData *)data type:(int)type {
_view=[view retain];
_printInfo=[printInfo copy];
if(type==NSPrintOperationPDFInRect || type==NSPrintOperationEPSInRect)
if(type==NSPrintOperationPDFInRect || type==NSPrintOperationEPSInRect || type==NSPrintOperationFile)
[_printInfo setPaperSize:rect.size];
_printPanel=[[NSPrintPanel printPanel] retain];
_showsPrintPanel=YES;
@ -43,7 +44,12 @@ static NSPrintOperation *_currentOperation=nil;
}
-initWithView:(NSView *)view printInfo:(NSPrintInfo *)printInfo {
return [self initWithView:view printInfo:printInfo insideRect:[view bounds] toData:nil type:NSPrintOperationPrinter];
int type = NSPrintOperationPrinter;
if ([[printInfo jobDisposition] isEqualToString: NSPrintSaveJob]) {
type = NSPrintOperationFile;
}
return [self initWithView:view printInfo:printInfo insideRect:[view bounds] toData:nil type:type];
}
-(void)dealloc {
@ -219,8 +225,7 @@ static NSPrintOperation *_currentOperation=nil;
if((context=(CGContextRef)[[_printInfo dictionary] objectForKey:@"_KGContext"])==nil)
return nil;
}
else if(_type==NSPrintOperationPDFInRect){
} else if(_type==NSPrintOperationPDFInRect){
NSDictionary *auxiliaryInfo=[NSDictionary dictionaryWithObject:[[_view window] title] forKey:(NSString *)kCGPDFContextTitle];
CGDataConsumerRef consumer=CGDataConsumerCreateWithCFData((CFMutableDataRef)_mutableData);
@ -229,10 +234,20 @@ static NSPrintOperation *_currentOperation=nil;
[(id)context autorelease];
CGDataConsumerRelease(consumer);
}
else
} else if (_type == NSPrintOperationFile) {
NSDictionary *auxiliaryInfo=[NSDictionary dictionaryWithObject:[[_view window] title] forKey:(NSString *)kCGPDFContextTitle];
NSString *filePath = [_printInfo.dictionary objectForKey:NSPrintSavePath];
CGDataConsumerRef consumer=CGDataConsumerCreateWithURL((CFURLRef)[NSURL fileURLWithPath:filePath]);
context=CGPDFContextCreate(consumer,&_insideRect,(CFDictionaryRef)auxiliaryInfo);
[(id)context autorelease];
CGDataConsumerRelease(consumer);
} else {
return nil;
}
return [NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:NO];
}

View File

@ -44,8 +44,10 @@ const char *O2ImageNameWithIntent(O2ColorRenderingIntent intent){
}
-(O2PDFObject *)encodeReferenceWithContext:(O2PDFContext *)context {
O2PDFStream *result=[O2PDFStream pdfStream];
O2PDFDictionary *dictionary=[O2PDFDictionary pdfDictionary];
O2PDFStream *result=[O2PDFStream pdfStream];
O2PDFDictionary *dictionary=[result dictionary];
const void *bytes = [self directBytes];
[dictionary setNameForKey:"Type" value:"XObject"];
[dictionary setNameForKey:"Subtype" value:"Image"];
@ -60,15 +62,62 @@ const char *O2ImageNameWithIntent(O2ColorRenderingIntent intent){
[dictionary setObjectForKey:"Mask" value:[_mask encodeReferenceWithContext:context]];
if(_decode!=NULL)
[dictionary setObjectForKey:"Decode" value:[O2PDFArray pdfArrayWithNumbers:_decode count:O2ColorSpaceGetNumberOfComponents(_colorSpace)*2]];
[dictionary setBooleanForKey:"Interpolate" value:_interpolate];
/* FIX, generate soft mask
[dictionary setBooleanForKey:"Interpolate" value:_interpolate];
/* FIX, generate soft mask for alpha data
[dictionary setObjectForKey:"SMask" value:[softMask encodeReferenceWithContext:context]];
*/
//[dictionary setNameForKey:"Filter" value:"FlateDecode"]; // Zipped
/* FIX
*/
return [context encodeIndirectPDFObject:result];
// TODO : we should at least flate encode the data
// And it would be nice if jpg data would stay jpg data (instead of an uncompress stream), as it does
// with Quartz and CGImage
// Export RGB bytes, without the alpha data, in the expected order
// TODO : support non 32 bits pixels, respect the premultiplied state, non-RGB pixels...
const uint8_t *ptr = bytes;
int alphaInfo = _bitmapInfo & kO2BitmapAlphaInfoMask;
BOOL hasAlpha = alphaInfo != kO2ImageAlphaNone;
BOOL alphaFirst = alphaInfo == kO2ImageAlphaPremultipliedFirst || alphaInfo == kO2ImageAlphaFirst || alphaInfo == kO2ImageAlphaNoneSkipFirst;
BOOL alphaLast = hasAlpha && (alphaFirst == NO);
BOOL bigEndian = _bitmapInfo & kO2BitmapByteOrder32Big;
BOOL littleEndian = bigEndian == NO;
int bytesPerPixel = _bitsPerPixel/8;
for (int i = 0; i < _height; i++, ptr += _bytesPerRow) {
const uint8_t *linePtr = ptr;
for (int j = 0; j < _width; j++, linePtr += bytesPerPixel) {
const uint8_t *pixelPtr = linePtr;
/*
AlphaFirst => The Alpha channel is next to the Red channel
(ARGB and BGRA are both Alpha First formats)
AlphaLast => The Alpha channel is next to the Blue channel
(RGBA and ABGR are both Alpha Last formats)
LittleEndian => Blue comes before Red
(BGRA and ABGR are Little endian formats)
BigEndian => Red comes before Blue
(ARGB and RGBA are Big endian formats).
*/
uint8_t r = 0, g = 0, b = 0;
if ((alphaFirst && bigEndian) || (alphaLast && littleEndian)) {
// Skip the alpha
++pixelPtr;
}
if (bigEndian) {
r = *pixelPtr++;
g = *pixelPtr++;
b = *pixelPtr++;
} else {
b = *pixelPtr++;
g = *pixelPtr++;
r = *pixelPtr++;
}
[[result mutableData] appendBytes:&r length: 1];
[[result mutableData] appendBytes:&g length: 1];
[[result mutableData] appendBytes:&b length: 1];
}
}
return [context encodeIndirectPDFObject:result];
}
@ -91,8 +140,6 @@ const char *O2ImageNameWithIntent(O2ColorRenderingIntent intent){
O2PDFStream *softMaskStream=nil;
O2Image *softMask=NULL;
NSLog(@"PDF Image=%@",dictionary);
if(![dictionary getIntegerForKey:"Width" value:&width]){
O2PDFError(__FILE__,__LINE__,@"Image has no Width");
return NULL;
@ -199,7 +246,6 @@ const char *O2ImageNameWithIntent(O2ColorRenderingIntent intent){
O2DataProviderRelease(provider);
O2ColorSpaceRelease(colorSpace);
NSLog(@"image=%@",image);
return image;
}

View File

@ -15,7 +15,6 @@ const NSString *kO2PDFContextTitle;
@interface O2PDFContext : O2Context {
O2DataConsumer *_dataConsumer;
NSMutableData *_mutableData;
NSMutableDictionary *_fontCache;
NSMapTable *_objectToRef;
NSMutableArray *_indirectObjects;
@ -30,12 +29,12 @@ const NSString *kO2PDFContextTitle;
NSMutableDictionary *_categoryToNext;
NSMutableArray *_textStateStack;
NSMutableArray *_contentStreamStack;
size_t _length;
}
-initWithConsumer:(O2DataConsumer *)consumer mediaBox:(const O2Rect *)mediaBox auxiliaryInfo:(NSDictionary *)auxiliaryInfo;
-(unsigned)length;
-(NSData *)data;
-(void)appendData:(NSData *)data;
-(void)appendBytes:(const void *)ptr length:(unsigned)length;

View File

@ -32,16 +32,20 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
#import <Onyx2D/O2Exceptions.h>
#import <Onyx2D/O2ClipState.h>
#import <Onyx2D/O2ClipPhase.h>
#import <Onyx2D/O2DataConsumer.h>
const NSString *kO2PDFContextTitle=@"kO2PDFContextTitle";
@implementation O2PDFContext
-(void)clipToState:(O2ClipState *)clipState
{
}
-initWithConsumer:(O2DataConsumer *)consumer mediaBox:(const O2Rect *)mediaBox auxiliaryInfo:(NSDictionary *)auxiliaryInfo {
[super init];
_dataConsumer=[consumer retain];
_mutableData=[[consumer mutableData] retain];
_fontCache=[NSMutableDictionary new];
_objectToRef=NSCreateMapTable(NSObjectMapKeyCallBacks,NSObjectMapValueCallBacks,0);
_indirectObjects=[NSMutableArray new];
@ -89,7 +93,6 @@ const NSString *kO2PDFContextTitle=@"kO2PDFContextTitle";
-(void)dealloc {
[_dataConsumer release];
[_mutableData release];
[_fontCache release];
NSFreeMapTable(_objectToRef);
[_indirectObjects release];
@ -107,29 +110,25 @@ const NSString *kO2PDFContextTitle=@"kO2PDFContextTitle";
}
-(unsigned)length {
return [_mutableData length];
}
-(NSData *)data {
return _mutableData;
}
-(void)appendData:(NSData *)data {
[_mutableData appendData:data];
return _length;
}
-(void)appendBytes:(const void *)ptr length:(unsigned)length {
[_mutableData appendBytes:ptr length:length];
_length += length;
O2DataConsumerPutBytes(_dataConsumer, ptr, length);
}
-(void)appendData:(NSData *)data {
[self appendBytes:[data bytes] length:[data length]];
}
-(void)appendCString:(const char *)cString {
[_mutableData appendBytes:cString length:strlen(cString)];
[self appendBytes:cString length:strlen(cString)];
}
-(void)appendString:(NSString *)string {
NSData *data=[string dataUsingEncoding:NSASCIIStringEncoding];
[_mutableData appendData:data];
[self appendData:data];
}
-(void)appendFormat:(NSString *)format,... {
@ -145,7 +144,7 @@ const NSString *kO2PDFContextTitle=@"kO2PDFContextTitle";
va_end(arguments);
}
-(void)appendPDFStringWithBytes:(const void *)bytesV length:(unsigned)length mutableData:(NSMutableData *)data {
-(void)appendPDFStringWithBytes:(const void *)bytesV length:(unsigned)length toObject:(id)data {
const unsigned char *bytes=bytesV;
BOOL hex=NO;
int i;
@ -183,7 +182,7 @@ const NSString *kO2PDFContextTitle=@"kO2PDFContextTitle";
}
-(void)appendPDFStringWithBytes:(const void *)bytes length:(unsigned)length {
[self appendPDFStringWithBytes:bytes length:length mutableData:_mutableData];
[self appendPDFStringWithBytes:bytes length:length toObject:self];
}
-(BOOL)hasReferenceForObject:(O2PDFObject *)object {
@ -247,7 +246,7 @@ const NSString *kO2PDFContextTitle=@"kO2PDFContextTitle";
}
-(void)contentPDFStringWithBytes:(const void *)bytes length:(unsigned)length {
[self appendPDFStringWithBytes:bytes length:length mutableData:[[_contentStreamStack lastObject] mutableData]];
[self appendPDFStringWithBytes:bytes length:length toObject:[[_contentStreamStack lastObject] mutableData]];
}
-(O2PDFObject *)referenceForFontWithName:(NSString *)name size:(float)size {
@ -287,8 +286,8 @@ const NSString *kO2PDFContextTitle=@"kO2PDFContextTitle";
const char *objectName=[[NSString stringWithFormat:@"%s%d",categoryName,[next intValue]] cString];
[category setObjectForKey:objectName value:pdfObject];
return [O2PDFObject_Name pdfObjectWithCString:objectName];
return [O2PDFObject_Name pdfObjectWithCString:objectName];
}
-(void)emitPath:(O2PathRef)path {
@ -340,6 +339,15 @@ const NSString *kO2PDFContextTitle=@"kO2PDFContextTitle";
}
}
- (void)translateCTM:(float)x:(float)y
{
[self contentWithFormat:@"1 0 0 1 %f %f cm ", x, y];
}
- (void)scaleCTM:(float)x:(float)y
{
[self contentWithFormat:@"%f 0 0 %f 0 0 cm ", x, y];
}
-(void)emitSaveGState {
[self contentWithString:@"q "];
}
@ -436,7 +444,7 @@ const NSString *kO2PDFContextTitle=@"kO2PDFContextTitle";
[self contentWithFormat:@"%g i ",gState->_flatness];
O2AffineTransform matrix=gState->_userSpaceTransform;
[self contentWithFormat:@"%g %g %g %g %g %g cm ",matrix.a,matrix.b,matrix.c,matrix.d,matrix.tx,matrix.ty];
[self contentWithFormat:@"%g %g %g %g %g %g cm ",matrix.a,matrix.b,matrix.c,matrix.d,matrix.tx,matrix.ty];
NSArray *clipPhases=[O2GStateClipState(gState) clipPhases];
@ -457,7 +465,7 @@ const NSString *kO2PDFContextTitle=@"kO2PDFContextTitle";
}
break;
case O2ClipPhaseMask:
case O2ClipPhaseMask: {
#if 0
O2PDFObject *pdfObject=[image encodeReferenceWithContext:self];
O2PDFObject *name=[self nameForResource:pdfObject inCategory:"XObject"];
@ -468,6 +476,7 @@ const NSString *kO2PDFContextTitle=@"kO2PDFContextTitle";
[self contentWithFormat:@"%@ Do ",name];
[self contentWithString:@"Q "];
#endif
}
break;
}
}
@ -545,16 +554,17 @@ const NSString *kO2PDFContextTitle=@"kO2PDFContextTitle";
-(void)drawImage:(O2Image *)image inRect:(O2Rect)rect {
[self emitSaveGState];
[self emitCurrentGState];
O2PDFObject *pdfObject=[image encodeReferenceWithContext:self];
O2PDFObject *pdfObject=[image encodeReferenceWithContext:self];
O2PDFObject *name=[self nameForResource:pdfObject inCategory:"XObject"];
[self contentWithString:@"q "];
// FIXME: enable these ctm changes
// [self translateCTM:rect.origin.x:rect.origin.y];
// [self scaleCTM:rect.size.width:rect.size.height];
[self emitCurrentGState];
[self emitSaveGState];
[self translateCTM:rect.origin.x:rect.origin.y];
[self scaleCTM:rect.size.width:rect.size.height];
[self contentWithFormat:@"%@ Do ",name];
[self contentWithString:@"Q "];
[self emitRestoreGState];
[self emitRestoreGState];
}
@ -584,10 +594,11 @@ const NSString *kO2PDFContextTitle=@"kO2PDFContextTitle";
for(i=0;i<[_indirectObjects count];i++){ // do not cache 'count', can grow during encoding
O2PDFxrefEntry *entry=[_indirectEntries objectAtIndex:i];
O2PDFObject *object=[_indirectObjects objectAtIndex:i];
unsigned position=[_mutableData length];
unsigned position=[self length];
[entry setPosition:position];
[self appendFormat:@"%d %d obj\n",[entry number],[entry generation]];
[self appendFormat:@"%d %d obj\n",[entry number],[entry generation]];
[object encodeWithPDFContext:self];
[self appendFormat:@"endobj\n"];
}
@ -616,6 +627,8 @@ const NSString *kO2PDFContextTitle=@"kO2PDFContextTitle";
-(void)close {
[self internIndirectObjects];
[self encodePDFObject:(id)_xref];
[_dataConsumer release];
_dataConsumer=nil;
}
@end

View File

@ -151,7 +151,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
for(i=0;i<count;i++){
O2PDFxrefEntry *entry=[_entriesInOrder objectAtIndex:i];
[encoder appendFormat:@"%010d %06d n\n",[entry position],[entry generation]];
[encoder appendFormat:@"%010d %05d n \n",[entry position],[entry generation]];
}
[_trailer setIntegerForKey:"Size" value:[[_entriesInOrder lastObject] number]+1];
[encoder appendCString:"trailer\n"];