Printing an NSImage - cocoa

I'm migrating Cocoa-Java code to Cocoa + JNI since Cocoa-Java is deprecated. The code prints an image stored in a file. The new Cocoa code is basically:
NSImage *image = [[NSImage alloc] initWithContentsOfFile:spoolFile];
if ( [image isValid] ) {
NSImageView *view = [[NSImageView alloc] init];
[view setImage:image];
[view setImageScaling:NSScaleProportionally];
NSPoint p;
NSSize s;
p.x = static_cast<float>( boundsX );
p.y = static_cast<float>( boundsY );
[view setBoundsOrigin:p];
s.width = static_cast<float>( boundsWidth );
s.height = static_cast<float>( boundsHeight );
[view setBoundsSize:s];
NSPrintInfo *info = [NSPrintInfo sharedPrintInfo];
[info setHorizontalPagination:NSClipPagination];
[info setVerticalPagination:NSClipPagination];
[info setHorizontallyCentered:NO];
[info setVerticallyCentered:NO];
p.x = static_cast<float>( boundsX );
p.y = static_cast<float>( [info paperSize].height - boundsHeight - boundsY );
[view translateOriginToPoint:p];
NSPrintOperation *printOp =
[NSPrintOperation printOperationWithView:view printInfo:info];
[printOp setShowsPrintPanel:NO];
[printOp runOperation];
}
Running this code eventually crashes with:
Thread 0 Crashed:
0 com.apple.AppKit 0x9484ac75 -[NSConcretePrintOperation(NSInternal) _tryToSetCurrentPageNumber:] + 345
1 com.apple.AppKit 0x948d88cf -[NSView(NSPrintingInternal) _printForCurrentOperation] + 524
2 com.apple.AppKit 0x948d85c5 -[NSConcretePrintOperation _renderView] + 358
3 com.apple.AppKit 0x9491f0c0 -[NSConcretePrintOperation runOperation] + 362
Why? How can I simply print an image that's stored in a file?

NSImageView *view = [[NSImageView alloc] init];
That's invalid. You need to use initWithFrame: to initialize a view. You'll probably want to pass a frame consisting of NSZeroPoint and the image's size.
As for the use of setBoundsOrigin: and setBoundsSize:: I'm not sure those will work, assuming you mean to crop the image. You can try them (after fixing the above problem), but I would feel safer to create a new image from the desired section of the old one. You would do this by creating an empty image of the desired size, locking focus on it, drawing the correct section of the old image at the origin in the new image, and unlocking focus on the new image, then giving the new image instead of the old to the image view.

Is that a typo the second time you assign to p.y? It doesn't look like you define info until 2 lines later...
Also, wouldn't it be simpler to use NSMakePoint() and NSMakeSize() by passing ints, instead of constructing them by hand and using static_cast<float>? That seems like a very C++ approach...
For example, something like this could work?
NSImage *image = [[NSImage alloc] initWithContentsOfFile:spoolFile];
if ([image isValid]) {
NSPrintInfo *info = [NSPrintInfo sharedPrintInfo];
[info setHorizontalPagination:NSClipPagination];
[info setVerticalPagination:NSClipPagination];
[info setHorizontallyCentered:NO];
[info setVerticallyCentered:NO];
NSImageView *view = [[NSImageView alloc] init];
[view setImage:image];
[view setImageScaling:NSScaleProportionally];
[view setBoundsOrigin:NSMakePoint(boundsX, boundsY)];
[view setBoundsSize:NSMakeSize(boundsWidth, boundsHeight)];
[view translateOriginToPoint:NSMakePoint(boundsX, [info paperSize].height -
boundsHeight - boundsY)];
NSPrintOperation *printOp = [NSPrintOperation printOperationWithView:view printInfo:info];
[printOp setShowsPrintPanel:NO];
[printOp runOperation];
}

Related

Centering text in OS X Screensaver

I'm working on implementing a (very) simple screensaver using the Screensaver framework in OS X 10.10. Positioning on the center of the screen and displaying a two-line text works without problem, but setting the alignment to NSCenterTextAlignment somehow doesn't (the text is always displayed left-aligned).
- (void)animateOneFrame
{
// calculate font size based on screen
NSSize size = [self bounds].size;
CGFloat fontsize = size.height / 11;
// set text
NSMutableParagraphStyle *centredStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
[centredStyle setAlignment:NSCenterTextAlignment];
NSDictionary *textAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
[NSFont fontWithName:#"Futura" size:fontsize], NSFontAttributeName,
[NSColor orangeColor], NSForegroundColorAttributeName,
centredStyle, NSParagraphStyleAttributeName,
nil];
NSString *theText = #"Simple text spanning\ntwo lines";
// position text on screen
NSRect boundingRect = [theText boundingRectWithSize:size options:0 attributes:textAttributes];
NSPoint point = NSMakePoint((size.width - boundingRect.size.width) / 2.0,
(size.height - boundingRect.size.height) / 2.0);
[theText drawAtPoint: point withAttributes: textAttributes];
}
Any pointers on how to solve this are appreciated.
PS: I know that I don't need to put everything into animateOneFrame but for the moment the goal is to get it working at all:-)

Trying to get color value of pixel

I'm trying to read the color value of a pixel in a tif image but I can't fihure out the correct way to do it. I'm using OSX and my approach is as follows:
NSImage *picture = [[NSImage alloc] initWithContentsOfFile:#"bais2.tif"]; //file is located in resoureces folder.
NSBitmapImageRep *imageRep = [[picture representations] objectAtIndex:0];
NSColor* color = [imageRep colorAtX:10 y:10];
NSLog(#"%f %f, %f", [color redComponent], [color blueComponent], [color greenComponent]);
The problem is that for some reason the logged values in NSLog always becomes 0.0000000....
I have also tried to use:
NSBitmapImageRep* imageRep = [[NSBitmapImageRep alloc] initWithData:[picture TIFFRepresentation]];
instead of [[picture representations] objectAtIndex:0] but the result is the same.
I get no error messages or warnings, but I think there is something wrong when i load the picture?
Please help me, what am i doing wrong? And is there a better way to read pixel color data?
Your error is here:
NSImage *picture = [[NSImage alloc] initWithContentsOfFile:#"bais2.tif"];
-------------------------------------------------------------------^^^^
You can use:
NSImage *picture= [NSImage imageNamed:#"bais2.tiff"];
Or :
NSImage *picture = [[NSImage alloc] initWithContentsOfFile:#"bais2.tiff"];
NSData *someNSData = [Image TIFFRepresentation];
NSBitmapImageRep *someNSBitmapImageRepData = [[NSBitmapImageRep alloc] initWithData:someNSData];
NSSize imageSizee = [someNSBitmapImageRepData size];
CGFloat y = imageSize.height - 100.0;
NSColor* color = [someNSBitmapImageRepData colorAtX:100.0 y:y];
NSLog(#"color = %#",color);
output : color = NSCalibratedRGBColorSpace 1 1 1 1

NSTextField width and autolayout

I am trying to create an NSTextField programmatically.
I want to use this NSTextField with auto layout, so its width will be defined automatically to display the entire line (there is only one line of text).
The problem is that textField.intrinsicContentSize and textField.fittingSize are both have -1 and 0 values for the horizontal coordinate, as the output of the code below is:
textField.intrinsicContentSize={-1, 21}
textField.fittingSize={0, 21}
The code:
NSTextField* textField = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 10, 24)];
NSString* str = #"One line of text here.";
[textField setStringValue: str];
[textField setTranslatesAutoresizingMaskIntoConstraints:NO];
[textField invalidateIntrinsicContentSize];
NSLog(#"textField.intrinsicContentSize=%#", NSStringFromSize(textField.intrinsicContentSize));
NSLog(#"textField.fittingSize=%#", NSStringFromSize(textField.fittingSize));
This makes the text field to have a zero width assigned by auto layout.
What should I do to get meaningful values for the fittingSize and intrinsicContentSize properties of the text field so they reflect the content of the text field?
Another way to compute the text field optimal width without using fittingSize is using the following code (replacing John Sauer's sizeTextFieldWidthToFit method):
- (void) sizeTextFieldWidthToFit {
[textField sizeToFit];
CGFloat newWidth = NSWidth(textField.frame);
textFieldWidthConstraint.constant = newWidth;
}
You can set the priority of textFieldWidthConstraint to be lower than the priority of another inequality constrain the represent your requirement to a minimal size of 25.
I can't explain why NSTextField's intrinsicContentSize's widths are -1, but I was able to calculate one based off its attributedString's size. Here's how I created the NSTextField:
// textField is an instance variable.
textField = [NSTextField new] ;
textField.translatesAutoresizingMaskIntoConstraints = NO ;
[view addSubview:textField] ;
NSDictionary* viewsDict = NSDictionaryOfVariableBindings(view, textField) ;
// textFieldWidthConstraint is an instance variable.
textFieldWidthConstraint = [NSLayoutConstraint constraintsWithVisualFormat:#"H:[textField(==0)]" options:0 metrics:nil views:viewsDict] [0] ;
[view addConstraint:textFieldWidthConstraint] ;
NSNumber* intrinsicHeight = #( textField.intrinsicContentSize.height ) ;
[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[textField(==intrinsicHeight)]" options:0 metrics:NSDictionaryOfVariableBindings(intrinsicHeight) views:viewsDict]] ;
// Position textField however you like.
[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[textField]" options:0 metrics:nil views:viewsDict]] ;
[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-[textField]" options:0 metrics:nil views:viewsDict]] ;
[self sizeTextFieldWidthToFit] ;
// set textField's delegate so that sizeTextFieldWidthToFit is called when textField's text changes.
textField.delegate = self ;
And here are the methods that resize it as its text changes:
- (void) controlTextDidChange:(NSNotification*)aNotification {
if ( aNotification.object == textField )
[self sizeTextFieldWidthToFit] ;
}
- (void) sizeTextFieldWidthToFit {
// I'd like the width to always be >= 25.
CGFloat newWidth = 25 ;
if ( ! [textField.stringValue isEqualToString:#""] ) {
NSDictionary* attributes = [textField.attributedStringValue attributesAtIndex:0 effectiveRange:nil] ;
NSSize size = [textField.stringValue sizeWithAttributes:attributes] ;
newWidth = MAX(newWidth, size.width + 10) ;
}
textFieldWidthConstraint.constant = newWidth ;
}
I'm adding this here because these answers are old and I couldn't find everything needed to get NSTextField to automatically wrap and play nice using autolayout and NSConstraints in a single answer.
With these settings the text will:
Remain in the horizontal boundaries set by the constraints.
Wrap automatically on word boundaries.
Automatically adjust its height as required.
Push down anything below (if attached by constraints).
This is the code to make it just work:
NSTextField *instructions = [NSTextField labelWithString:#"Some very long text..."];
instructions.translatesAutoresizingMaskIntoConstraints = NO;
instructions.autoresizesSubviews = YES;
instructions.usesSingleLineMode = NO;
instructions.lineBreakMode = NSLineBreakByWordWrapping;
[instructions cell].wraps = YES;
[instructions cell].scrollable = NO;
instructions.maximumNumberOfLines = 10;
instructions.preferredMaxLayoutWidth = 400.0;
[self addSubview:iCloudInstructions];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[instructions]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(instructions)]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[instructions]-(20)-[button]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(instructions, instructions)]];

CIImage drawing EXC_BAD_ACCESS

So, I have a CIImage that I'm attempting to draw in an NSView's -drawRect method.
This is the line of code that I call to draw the image:
[outputCoreImage drawInRect: [self bounds]
fromRect: originalBounds
operation: NSCompositeSourceOver
fraction: 1];
outputCoreImage, originalBounds, and [self bounds] are all non-nil, and indeed are their respective expected values. On Lion (OS X 10.7), this worked fine, however on Mountain Lion (OS X 10.8) I receive an EXC_BAD_ACCESS on this line. If I walk up the stack, I find that the internal function call that breaks is on CGLGetPixelFormat.
frame #0: 0x00007fff99c3b26d OpenGL`CGLGetPixelFormat + 27
frame #1: 0x00007fff8fa98978 CoreImage`-[CIOpenGLContextImpl createAccelContext] + 335
frame #2: 0x00007fff8fa98d30 CoreImage`-[CIOpenGLContextImpl updateContext] + 111
frame #3: 0x00007fff8fa9a865 CoreImage`-[CIOpenGLContextImpl _lockfeContext] + 25
frame #4: 0x00007fff8fa7e4ac CoreImage`-[CIContextImpl setObject:forKey:] + 120
frame #5: 0x00007fff8fa9881c CoreImage`-[CIOpenGLContextImpl setObject:forKey:] + 259
frame #6: 0x00007fff90db93e3 libCGXCoreImage.A.dylib`cgxcoreimage_instance_render + 1477
frame #7: 0x00007fff97074ac6 CoreGraphics`CGSCoreImageInstanceRender + 32
frame #8: 0x00007fff947c89cc libRIP.A.dylib`ripc_AcquireCoreImage + 1344
frame #9: 0x00007fff947b8a00 libRIP.A.dylib`ripc_DrawShading + 9680
frame #10: 0x00007fff96cb2b2b CoreGraphics`CGContextDrawShading + 51
frame #11: 0x00007fff8fa78237 CoreImage`-[CICGContextImpl render:] + 918
frame #12: 0x00007fff8fa7d76a CoreImage`-[CIContext drawImage:inRect:fromRect:] + 1855
frame #13: 0x00007fff9897b8f3 AppKit`-[CIImage(NSAppKitAdditions) drawInRect:fromRect:operation:fraction:] + 243
I have zombies, guard malloc, and log exceptions all turned on, and none of them return any useful information.
Other OpenGL Testing:
I added this chunk:
CIImage *anImage = [[CIImage alloc] init];
[[[self window] contentView] lockFocus];
{
[anImage drawInRect: [[self window] frame]
fromRect: [[self window] frame]
operation: NSCompositeSourceOver
fraction: 1];
}
[[[self window] contentView] unlockFocus];
to the -windowDidLoad method of my NSWindowController. It runs without issue.
I added this chunk:
NSData *data = [NSData dataWithContentsOfURL: [NSURL URLWithString: #"http://cache.virtualtourist.com/14/684723-Red_Square_Moscow.jpg"]];
CIImage *anImage = [[CIImage alloc] initWithData: data];
[self lockFocus];
{
[anImage drawInRect: [self bounds]
fromRect: [anImage extent]
operation: NSCompositeSourceOver
fraction: 1];
}
[self unlockFocus];
to another NSView's -drawRect. I received the same error on CGLGetPixelFormat.
Radar:
The folks over at #macdev seem to believe that this is in an OS issue, and I'm not inclined to disagree. I've filed a radar (rdar://11980704) explaining the problem and linking back to this question.
Workaround:
CGContextRef contextReference = [[NSGraphicsContext currentContext] graphicsPort];
CIContext *coreImageContext = [CIContext contextWithCGContext: contextReference
options: #{kCIContextUseSoftwareRenderer : #YES}];
[coreImageContext drawImage: outputCoreImage
inRect: [self bounds]
fromRect: originalBounds];
It looks like forcing the image to draw using the software renderer "solves" the problem. It's a bit blurry and slow, but it doesn't crash.
Resolved in 10.8.1
The folks over at #macdev seem to believe that this is in an OS issue, and I'm not inclined to disagree. I've filed a radar (rdar://11980704) explaining the problem and linking back to this question.
This might be the first time your app calls into any OpenGL routine which is probably doing some first-run initialization or checks.
For an experiment, try adding a little OpenGL code earlier in your app and see if that crashes as well. Try to think about why calling OpenGL might causing a crash.

How to align a subview to the center of a parent NSView

I'm developing a program in which I am programmatically adding an NSImageView to a custom NSView class. While creating the image-view, I am passing the frame of the parent container.
-(NSImageView *)loadNSImage:(NSString *)imageName frame:(NSRect)frame{
imageName = [[NSBundle mainBundle] pathForResource:imageName ofType:#"png"];
NSImage *image = [[NSImage alloc] initWithContentsOfFile:imageName];
NSImageView *imageView = [[NSImageView alloc]initWithFrame:frame];
[imageView setImage:image];
[imageView setImageScaling:NSImageScaleProportionallyUpOrDown];
[imageView setAutoresizingMask:NSViewHeightSizable | NSViewWidthSizable | NSViewMaxXMargin | NSViewMaxYMargin | NSViewMinXMargin | NSViewMinYMargin];
[imageView setImageAlignment:NSImageAlignCenter];
[image release];
return imageView;
}
Then I am using the addSubView method to add this into the Custom View. The problem is that the image sticks to the bottom-left of the parent view. How can I place this image in the center of the parent view?
I have tried adding an offset to the frame origin, but that doesn't really work when the window is resized, or if an image with a different size is loaded.
Any help would be appreciated.
I'm not sure if this is the best way to do it, but I just do simple math when I want to center a subview inside its parent.
You need to set all the margins to be auto-resizable if you want it to stay centered.
[subview setFrameOrigin:NSMakePoint(
round((NSWidth([parentView bounds]) - NSWidth([subview frame])) / 2),
round((NSHeight([parentView bounds]) - NSHeight([subview frame])) / 2)
)];
[subview setAutoresizingMask:NSViewMinXMargin | NSViewMaxXMargin | NSViewMinYMargin | NSViewMaxYMargin];
This is just calculating the margins required to get a centered origin.
If you need the centered frame before you invoke initWithFrame: then just use the above logic to compute the frame origin values.
Swift 5 #d11wtq's answer
let x = (parentView.bounds.width - subviewWidth) * 0.5
let y = (parentView.bounds.height - subviewHeight) * 0.5
let f = CGRect(x: x, y: y, width: subviewWidth, height: subviewHeight)
let subview = NSView(frame: f)
subview.autoresizingMask = [.minXMargin, .maxXMargin, .minYMargin, .maxYMargin ]
Objective - C in macOS Catalina , Version 10.15.3
more readable #d11wtq's answer
CGFloat x = (NSWidth(parentView.bounds) - subviewWidth) * 0.5;
CGFloat y = (NSHeight(parentView.bounds) - subviewHeight) * 0.5;
CGRect f = CGRectMake(x, y, subviewWidth, subviewHeight);
subview = [[NSView alloc] initWithFrame: f];
subview.autoresizingMask = NSViewMinXMargin | NSViewMaxXMargin | NSViewMinYMargin | NSViewMaxYMargin;
Try This.
in .h
#property (strong, nonatomic) IBOutlet UIView *CoinsPurchaseView;
in .m
self.CoinsPurchaseView.center = self.view.center;
[self.view addSubview:self.CoinsPurchaseView];

Resources