mirror of
https://github.com/darlinghq/darling-cocotron.git
synced 2024-11-27 14:00:22 +00:00
Basic support for Image in PDF Context + support for printing to a PDF file
This commit is contained in:
parent
b5ab411409
commit
235e290936
@ -73,7 +73,6 @@ id NSApp=nil;
|
||||
|
||||
+(void)initialize {
|
||||
if(self==[NSApplication class]){
|
||||
|
||||
[NSClassFromString(@"Win32RunningCopyPipe") performSelector:@selector(startRunningCopyPipe)];
|
||||
}
|
||||
}
|
||||
|
@ -793,7 +793,6 @@ forSaveOperation:(NSSaveOperationType)operation
|
||||
if(operation==nil){
|
||||
return;
|
||||
}
|
||||
|
||||
[operation setShowsPrintPanel:showPrintPanel];
|
||||
[operation runOperation];
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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";
|
||||
|
@ -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];
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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"];
|
||||
|
Loading…
Reference in New Issue
Block a user