Printing Off-screen PDFViews - objective-c

I have a situation where I want to print a multi-page PDF. While I could use the PDFKit utility classes and/or quartz functions to get the information to manually write drawing/pagination code for a NSView subclass, I had thought that quicker alternative would be to create an off-screen PDFView and tell it to print itself. When I tried this solution, the print dialog didn't go away, all of the print settings controls on the right half of the print dialog disappeared, and the application froze.
I then wrote a tiny test application with the following method that illustrates the problem. When the test program is compiled without the USE_PDF_VIEW preprocessor macro defined, the blank view displays fine. If USE_PDF_VIEW is defined, the document doesn't print, most of the print dialog controls disappear, and the app freezes. While I have other ways of accomplishing my goal, I'm curious as to why this shortcut doesn't work. Is there something about Cocoa drawing I still don't understand? Am I banging into Apple Voodoo Magic(tm) behind the scenes that makes PDFView behave in a completely different way than other NSViews?
- (void)printMyStuff:(id)sender {
NSPrintInfo *currInfo = [NSPrintInfo sharedPrintInfo];
#ifdef USE_PDF_VIEW
PDFView *pdfView = [[PDFView alloc] init];
PDFDocument *pdfDoc = [[PDFDocument alloc] initWithURL:[NSURL fileURLWithPath:#"/Users/wls/Documents/my_document.pdf"]];
[pdfView setDocument: pdfDoc];
[pdfView printWithInfo:currInfo autoRotate:YES];
#else
NSView *myView = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, 500, 500)];
NSPrintOperation *myop = [NSPrintOperation printOperationWithView:myView printInfo:currInfo];
[myop runOperation];
#endif
}

Had the exact same problem.
The PDFView needs to be added to a NSWindow in order for printWithInfo:autoRotate: to work (atleast in my case), otherwise the printing controls go blank or won't work.
Here's the complete code:
PDFView *vDoc = [[PDFView alloc] init];
[vDoc setDocument:pdfDoc];
[vDoc setAutoScales: YES];
[vDoc setDisplaysPageBreaks: NO];
NSWindow *wnd = [[NSWindow alloc] init];
[wnd setContentSize:vDoc.frame.size];
[wnd setContentView:vDoc];
[vDoc printWithInfo:printInfo autoRotate:YES];
[wnd release];
[vDoc release];

Building on alex-i's excellent answer, I added the following lines so the print dialog showed up in a user-friendly location:
NSRect windowRect = self.window.frame;
NSPoint printTopLeftPoint = NSMakePoint(CGRectGetMidX(windowRect), CGRectGetMaxY(windowRect));
[wnd setFrameTopLeftPoint:printTopLeftPoint];
My self.window is for my current window controller, not the temporary window.

I like alex-i's answer because it does not use private APIs. But in my case, I already have a window (and I suppose in most cases you would!), so I figured I would use that window instead of creating one. Here is what I ended up doing, using swift:
func print(_ pdfDocument: PDFDocument, using window: NSWindow) {
// create a hidden pdf view with the document
let pdfView = PDFView()
pdfView.document = pdfDocument
pdfView.autoScales = true
pdfView.displaysPageBreaks = false
pdfView.frame = NSMakeRect(0.0, 0.0, 50.0, 50.0)
pdfView.isHidden = true
// add the view to the window and print
window.contentView.addSubview(pdfView)
pdfView.print(nil)
pdfView.removeFromSuperview()
}

PDFView is a subclass of NSView. The designated initializer for NSView is -initWithFrame: ... if you don't use -initWithFrame: strange things can happen. Since PDFView has no other designated initializers, -initWithFrame: is it. I'm guessing that's at least part of your problem.
Another part may be memory related. Are you using garbage collection or not? If you are, you're not keeping a reference to your PDFView anywhere, so may be getting deallocated. If you aren't using garbage collection, you're leaking your PDFView (also because you keep no reference to it, so you can release it when you're done). Same with your myView NSView instance ... you're leaking it if you're not using GC.

Related

brightness filter is not working in live image GPUImage framework objective c [duplicate]

I've gone through the obj-c GPUImage framework, and as per the example in the documentation, I added the following snippet with intention to display filtered live video:
CGRect mainScreenFrame = [[UIScreen mainScreen] applicationFrame];
GPUImageVideoCamera *videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPreset640x480 cameraPosition:AVCaptureDevicePositionBack];
videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait;
GPUImageFilter *customFilter = [[GPUImageFilter alloc] initWithFragmentShaderFromFile:#"PositionColor"];
GPUImageView *filteredVideoView = [[GPUImageView alloc] initWithFrame:mainScreenFrame];
// Add the view somewhere so it's visible
[self.view addSubview:filteredVideoView];
[videoCamera addTarget:customFilter];
[customFilter addTarget:filteredVideoView];
[videoCamera startCameraCapture];
It works, but instead of video i get a single still image. I've reviewed it and looked at examples and can't really pinpoint why it isn't working correctly. Any help would be much appreciated.
Thanks!
You need to hang on to your GPUImageVideoCamera somewhere. If the above is placed in a method, and ARC is enabled for the project, the GPUImageVideoCamera instance will be deallocated the instant that method exits. This will terminate the video capture and could lead to other unsettling artifacts.
Make a GPUImageVideoCamera instance variable in your class, instead, and use that for the above. You're also going to need that so that you can pause and stop the camera when done.

(SpriteKit Objective-C) Scene Stretched Out After Transition

When I transition from MainMenuScene.m to SceneLvl1.m, the transition works perfectly fine. But when transitioning back from SceneLvl1.m to MainMenuScene.m, all the sprites are distorted. They're stretched out to almost 3x their original width! Here's my transitioning code:
// Main Menu initialization.
MainMenuScene *mainMenu = [[MainMenuScene alloc] init];
// Move to the main menu.
[self.scene.view presentScene:mainMenu
transition:[SKTransition fadeWithColor:[SKColor blackColor]
duration:3.0]];
So, what could I be doing wrong, here?
This might sound a stupid question, but are you calling simply init in it?
Any scene initialization should use initWithSize, right?
Try replacing with
MainMenuScene *mainMenu = [[MainMenuScene alloc] initWithSize:self.view.frame.size];

Which one of the two NSTextViews has been edited? doCommandBySelector is always returns the first one

I'm desperate to find the answer, so I opened TextLayoutDemo sample project from Apple. The point is that: I have two NSTextViews for column view. Everything works fine, text I enter is successfully laying out in those two text views via single layout manager:
NSLayoutManager *twoColumnLayoutManager = [[NSLayoutManager alloc] init];
NSTextContainer *firstColumnTextContainer = [[NSTextContainer alloc] init];
NSTextContainer *secondColumnTextContainer = [[NSTextContainer alloc] init];
NSTextView *firstColumnTextView = [[NSTextView alloc] initWithFrame:NSMakeRect(0, 0, 240, 360) textContainer:firstColumnTextContainer];
firstColumnTextView.delegate = self;
NSTextView *secondColumnTextView = [[NSTextView alloc] initWithFrame:NSMakeRect(240, 0, 240, 360) textContainer:secondColumnTextContainer];
secondColumnTextView.delegate = self;
[firstColumnTextContainer setContainerSize:NSMakeSize(240, 360)];
[secondColumnTextContainer setContainerSize:NSMakeSize(240, 360)];
[twoColumnLayoutManager addTextContainer:firstColumnTextContainer];
[twoColumnLayoutManager addTextContainer:secondColumnTextContainer];
[twoColumnLayoutManager replaceTextStorage:[firstTextView textStorage]];
[[secondWindow contentView] addSubview:firstColumnTextView];
[[secondWindow contentView] addSubview:secondColumnTextView];
But my goal is to get to know in which one text views the user edits a text. If it's the left one, I need to call one method, but if it's the right one, I want to call another method. And it seems impossible to recognize the correct text view, because delegate always get notified by the first text view.
- (BOOL) textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector {
NSLog(#"edit: %#", textView);
return NO;
}
This method is always prints the first text view, even if I change text in the second one. And I see it's going according to docs, where Apple says there always will be just the first NSTextView in series.
But how can I solve my problem then?
Just tell me, if this solution is the one I am looking for. Because, in fact, it works just fine. The one thing I don't understand is why Cocoa text system is so tricky where it is not necessary?
- (void)textStorageDidProcessEditing:(NSNotification *)aNotification {
// that's the active text view
NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow];
NSTextView *activeTextView = (NSTextView *)[keyWindow firstResponder];
NSLog(#"%p", activeTextView);
}
UPDATE: this works only if user clicked the mouse button. Arrow keys do not update window's first responder:(

GPUImage displaying image instead of live video

I've gone through the obj-c GPUImage framework, and as per the example in the documentation, I added the following snippet with intention to display filtered live video:
CGRect mainScreenFrame = [[UIScreen mainScreen] applicationFrame];
GPUImageVideoCamera *videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPreset640x480 cameraPosition:AVCaptureDevicePositionBack];
videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait;
GPUImageFilter *customFilter = [[GPUImageFilter alloc] initWithFragmentShaderFromFile:#"PositionColor"];
GPUImageView *filteredVideoView = [[GPUImageView alloc] initWithFrame:mainScreenFrame];
// Add the view somewhere so it's visible
[self.view addSubview:filteredVideoView];
[videoCamera addTarget:customFilter];
[customFilter addTarget:filteredVideoView];
[videoCamera startCameraCapture];
It works, but instead of video i get a single still image. I've reviewed it and looked at examples and can't really pinpoint why it isn't working correctly. Any help would be much appreciated.
Thanks!
You need to hang on to your GPUImageVideoCamera somewhere. If the above is placed in a method, and ARC is enabled for the project, the GPUImageVideoCamera instance will be deallocated the instant that method exits. This will terminate the video capture and could lead to other unsettling artifacts.
Make a GPUImageVideoCamera instance variable in your class, instead, and use that for the above. You're also going to need that so that you can pause and stop the camera when done.

When should I deallocate memory, if all?

I’ve read all the iOS memory allocation/deallocation basics, but can’t find anything on the following:
I’ve created a small app, that pretty much lists a grid of UIButtons and clicking on any of those, it adds a UIScrollView to the current controller view (and adds a bunch of UIView and UIWebView’s inside etc).
On adding the UIScrollVIew, I also add a UIButton, that takes me back to the "home grid" and then removes the UIScrollView from the superview.
I release all the things I’m retaining/allocating etc and when I check the app with Instruments, it doesn’t show any memory leak.
But every time I tap on any of the UIButton objects, I allocate more memory (according to Instruments) and it keeps growing – and "reopening" the same kind of UIScrollView from a button always adds more memory allocations.
If I simulate a memory warning in the simulator, it deallocated a bit of the memory and I can then keep growing it again.
So here’s my question: should I bother with trying to deallocate this somehow manually? And if so, where should I actually do this? I’m quite new to Obj-C, so I think I have most of the basics covered, but more advanced topics still require some help.
Creating the grid:
UIScrollView *grid = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 1024, 768)];
[[self view] addSubview:grid];
[grid release];
Adding buttons (in a for loop):
UIButton *slotItem = [[UIButton alloc] initWithFrame:CGRectMake(((float) slotWidth * j), ((float) slotHeight * i), (float) slotWidth, (float)slotHeight)];
[grid addSubview:slotItem];
UIWebView *buttonWebThumb = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, slotWidth, slotHeight)];
[buttonWebThumb setBackgroundColor:[UIColor clearColor]];
[buttonWebThumb setOpaque:NO];
[buttonWebThumb loadHTMLString:[NSString stringWithFormat:#"%# %# %#", htmlTop, [[row objectAtIndex:j] objectAtIndex:1], htmlBottom] baseURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]]];
buttonWebThumb.scalesPageToFit = YES;
[slotItem addSubview:buttonWebThumb];
buttonWebThumb.userInteractionEnabled = NO;
buttonWebThumb.exclusiveTouch = NO;
[buttonWebThumb release];
[slotItem addTarget:self action:#selector(showPages:) forControlEvents:UIControlEventTouchUpInside];
[slotItem release];
showPages method then creates another UIScrollView and adds 1-10 separate UIWebView’s in there and adds a “close” button to the new UIScrollView.
You're probably seeing this because you aren't removing all elements from the view before leaving it.
For example, if you exit the view with a method like this, do this:
- (id) viewExitAction: (id) sender {
id elem;
for(elem in buttons) {
[elem removeSelfFromView];
}
// etc
}
This will automatically decrease the retain counts of these objects down to zero. (Provided you added them to the view and then released them, as you're supposed to.)
Otherwise, the viewDidUnload and dealloc methods never get called.
Dealloc is invoked indirectly through the release method, and you should never call it directly.NSObject Manual on dealloc