I've inherited some iOS code which opens a source PDF and creates a CGContextRef to which we draw a single page from the source document. The problem is that there are certain pages with one document, our help document unfortunately, which causes this code to crash.
The end goal is to cache 8 pages at a time to improve the user experience.
CFMutableDataRef consumerData = CFDataCreateMutable(kCFAllocatorDefault, 0);
CGDataConsumerRef contextConsumer = CGDataConsumerCreateWithCFData(consumerData);
CGPDFPageRef page = CGPDFDocumentGetPage(sourceDocument, pageNumber);
const CGRect mediaBox = CGPDFPageGetBoxRect(page, kCGPDFCropBox);
CGContextRef pdfOutContext = CGPDFContextCreate(contextConsumer, &mediaBox, NULL);
CGContextDrawPDFPage(pdfOutContext, page); //If I comment out this line, no exception occurs
CGPDFPageRelease(page);
CGPDFContextEndPage(pdfOutContext);
CGPDFContextClose(pdfOutContext); //EXC_BAD_ACCESS
CGContextRelease(pdfOutContext);
(This is a simplified version of the code, the original opens a source document and a page, checks for null on page and ctx, and then writes ctx to a new document.)
There's no issue if, instead of drawing to a PDF context, I draw to a UIGraphics context created thusly:
CGContextRef graphicsContext = UIGraphicsGetCurrentContext();
There is also no issue when I draw other things to the PDF context.
Plus, this works for 99% of the documents and for 75% of the pages within the offending document. The offending document renders correctly in several PDF viewers.
So I don't think there's a memory issue on my part. I'm fairly confident that there is something within the CGPDF code that is buggy (and I say that only after spending a week trying to resolve this issue).
My question is, is there some other way I should/could be doing this?
There is enough evidence that this is a bug introduced in iOS5 for us to work around the issue rather than trying to solve it. So we ended up removing the caching. It's only marginally slower on an iPad 1 than it was with caching for a 200 page document, so the product manager decided that was acceptable (when compared to simply crashing).
We also tried writing the doc to an image and displaying that, but it wasn't any faster and produced a result of inferior quality (especially when zooming).
EDIT
Submitted the bug to Apple. It turns out to have already been reported. The original bug is 10428451 and is being looked at by their engineers.
You're missing CGPDFContextBeginPage.
Related
I followed the guide at this URL: http://developers.itextpdf.com/content/itext-7-jump-start-tutorial/chapter-6-reusing-existing-pdf-documents
Following that guide, I had a problem where some content from the PDF was not copied into the destination PDF when using copyAsFormXObject (which I submitted a support ticket for). An alternative I found in the meantime was that I could use the PdfDocument's copyPagesTo method and simply open the page that was copied with getPage on the destination PDF. From that, I can create a PdfCanvas from the existing page and do our transformations (such as scaling) on the object.
This seems to work exactly as the code in the aforementioned guide with the exception that the PDFs I found where content wasn't copied, the content now appears to be copied.
Are there any drawbacks to using the copyPagesTo method to copy the content as opposed to what the guide suggests (copyAsFormXObject)? Performance, memory, or extraneous non-visible content, etc.?
Code that exhibits this problem:
PdfDocument pdf = new PdfDocument(new PdfWriter(dest));
PdfDocument origPdf = new PdfDocument(new PdfReader(src));
PdfPage origPage = origPdf.getPage(1);
PdfPage page = pdf.addNewPage();
PdfCanvas canvas = new PdfCanvas(page);
PdfFormXObject pageCopy = origPage.copyAsFormXObject(pdf);
canvas.addXObject(pageCopy, 0, 0);
pdf.close();
origPdf.close();
Code that does not:
PdfDocument pdf = new PdfDocument(new PdfWriter(dest));
PdfDocument origPdf = new PdfDocument(new PdfReader(src));
origPdf.copyPagesTo(1,2,pdf);
pdf.close();
origPdf.close();
I've provided code and answers for the specific problem on your support ticket.
As for the difference between copyToPages() and copyAsFormXObject() for copying pages:
copyToPages() is a high level method that copies over the entire page, maintaining all structure and adding any applicable resources to the new document.
With copyAsFormXObject(), you first need to transform the page to an XObject, essentially turning it into an appearance stream. If this page needs additional settings or resources to be displayed correctly, such as a different page size or fonts that were not stored on the page itself, they need to be manually set or added. XObject are always added at absolute positions, so this needs to be specified too.
While copying using low-level methods such as XObjects grants a lot more control over what the result can look like, they come with their own dangers and pitfalls. For ubiquitous tasks such as copying pages, it is better to use the high-level methods to avoid such possible problems.
EDIT:
We've decided that this behaviour is a bug and that 'copyAsFormXObject()' should include the used resources even if they're stored at the /Pages level. This will be fixed in a later release of iText
I'm trying to create an app that has an todays widget extension. This app should simply access a folder full of pictures, load them and draw them to the screen. One picture every hour. The widget should do the same, widget wise.
While loading an image in the main app and drawing it to the screen works flawlessly doing this:
NSString *tildePath = #"~/Documents/Adrian/Art/desertMovie.jpg";
NSString *path = [tildePath stringByExpandingTildeInPath];
_image = [[NSImage alloc] initWithContentsOfFile:path];
the extension completely messes up the path. It uses the same code as the main application but obscures the path completely.
From:
~/Documents/Adrian/Art/desertMovie.jpg
to:
/Users/Adrian/Library/Containers/ac.at.hulala.AK.ImageViewer2.ImageViewerWidget/Data/Documents/Adrian/Art/desertMovie.jpg
If I use a path without "~" the path stays the same but the image still doesn't get loaded.
Can someone tell my what I have to do that it accesses the same folder the main application does?
EDIT:
Archiving the app and exporting it seems to fix that problem. However I still would like to know how to make this working while developing.
EDIT2:
This is so weird! When working with the Todays Extension Widget a path like #"~/Pictures would lead to /Users/Me/Libraries/Containers/ac.at.ImageViewerWidget/Data/ like what the f***, why!?
When I force it to use the path #"/Users/Me/Documents/Pictures and run it the Memory Usage steadily increases until it reaches the limit and my system goes to hell. I really don't get that. I guess that the widget isn't finding any files so it tries to load the whole ssd. I guess. I really don't know whats the problem here.
Can someone please lead me into the light?
Not that xcode would tell you if something is wrong with your app. It will just allocate your whole memory and burn your cpu,
UNTIL,
you realize that the problem is caused by the lack of folder accessing permissions. Geezuz!
Add folder access permissions to your application if you want to access something!
when trying to create an Image from a signed PDF Page, the resulting image shows the signatures but the signatures are not displayed correctly.
For example, the original contains two signatures next to each other in the bottom section.
In the resulting image the signatures look like they have been scaled up and are overlapping.
Furthermore, there's a signature in the top right corner. This signature looks scaled up in the resulting image and is cut off to the right. What is happening here? What am I doing wrong? I'm pretty new to working with PDFs on this level.
Hope that makes sense. Please see below for the differences (I've cut out other content).
Here's the code I'm using:
List<PDPage> pages = inputDocument.getDocumentCatalog().getAllPages();
PDPage page = pages.get(0);
BufferedImage image = page.convertToImage(BufferedImage.TYPE_INT_RGB, PDF_RESOLUTION);
String fileName = "converted_image_" + (i + 1);
ImageIOUtil.writeImage(image, "png", fileName, BufferedImage.TYPE_INT_RGB, PDF_RESOLUTION);
here's the original
and now the distorted version
As suggested by Tilman Hausherr, I was using the current 1.8.x stable release which has problems with annotations appearances. This led to the seen behaviour. Testing with the current 2.0 SNAPSHOT solves this problem.
Now we are eagerly awaiting the release of 2.x :)
From what I've seen, they totally reworked how creating images from a PDF(Page) should be done so I'm not sure about the probability of a backport.
Hope that helps for anyone else coming across this.
I'm building a QuickLook plugin. I want to change the width of the windows that pops up when user hits the spacebar.
I've read there are two keys in the info.plist file of the project where height and width are customisable. Even if I change those values I can't get the size of the preview windows to my desired one.
I don't know what else to try. Any idea?
Thanks!
Thought I'd dig a little on this. I have not tried any of the following suggestions, so nobody get their hopes up. I'll assume you're using the generator callback:
OSStatus (*GeneratePreviewForURL)(
void *thisInterface,
QLPreviewRequestRef preview,
CFURLRef url,
CFStringRef contentTypeUTI,
CFDictionaryRef options
);
Before anything else, you might manually check the options dictionary argument and verify that the kQLPreviewPropertyWidthKey and kQLPreviewPropertyHeightKey keys are indeed mapped to the desired CFNumber values.
Referring to each of these properties, the Apple QuickLook programming guide says:
Note that this property is a hint; Quick Look might set the width
automatically for some types of previews. The value must be
encapsulated in a CFNumber object.
(Edit: If your preview representation is flexible, you might try finding a preview type for which QuickLook honors your size hints, as per the statement above. Just a thought.)
Running nm on the QuickLook framework binary revealed some undocumented kQLPreviewProperty-- constants as well as the aforementioned width and height keys. One that caught my attention was kQLPreviewPropertyAutoSizeKey. Recalling Apple's statement about ignoring the hints to set the size automatically, this might be significant? Following the convention in QuickLook.framework/Headers/QLBase.h, you might try declaring
extern const CFStringRef kQLPreviewPropertyAutoSizeKey;
Then you could try associating a CFNumber 0 with that property key in the options dictionary. There are other undocumented keys of note, such as kQLPreviewPropertyAttributesKey.
Back to the Info.plist you mentioned, Apple says about those keys QLPreviewWidth and QLPreviewHeight:
This number gives Quick Look a hint for the width (in points) of
previews. It uses these values if the generator takes too long to
produce the preview. (emphasis added)
This is where someone makes the terrible suggestion of calling sleep() in your generator. But I'm perplexed as to why Apple would make following the size hints dependent on the generator latency. (?)
Edit: Also note the above statement says the Info.plist hints must be expressed in points (not pixels), a unit dependent on the user's screen resolution.
Recently I was developing a Quick Look Plugin myself which uses HTML+CSS and faced the same problem.
The solution for my was to test the plugin not within Xcode and qlmanage as the executable but instead to try the real .qlgenerator from my user library.
When invoking the generator from my user library, the Quick Look window was resized exactly the way I specified in the *-Info.plist.
I've run into the same problem, and may offer some clues: In my case I'm generating an image quick look preview for my custom file format. I initiate the preview context to draw my preview into using
CGContextRef QLPreviewRequestCreateContext(QLPreviewRequestRef preview, CGSize size, Boolean isBitmap, CFDictionaryRef properties);
The curious thing is that if I set isBitmap to true, quick look adjusts the preview panel size to the size specified for the context (up to a certain size at least). But if you set isBitmap to false, it seems to disregard the context size and instead always shows a full size preview panel with the vector graphics image scaled to cover the entire panel.
So, if you use a bitmap graphical preview context, it seems the preview panel will be set to the size of the context you specify. However, I haven't found any way to set the size of the panel when using a vector graphic preview context (which is what I want).
I'm trying to print a PDFDocument that I am constructing from a series of images. In case it matters, I'm doing all of this from within a Mozilla plugin.
I create the PDFDocument, and put it into a PDFView, then I call
[printView printWithInfo: [NSPrintInfo sharedPrintInfo] autoRotate: YES];
The print dialog comes up (as a separate window, instead of panel. I assume that that comes from being inside a mozilla window, so I wasn't too worried about it. The dialog shows my document, and I can page through it correctly, and everything looks good.
However, when I hit "Print" the dropdown with "Layout" etc becomes empty, and the view under that becomes empty. The window doesn't disappear, and the document doesn't print. Hitting Cancel does exactly the same thing. The only thing I can do then is force-quit Mozillla.
I based the program off of PDFKitLinker2 from the apple dev site, and that program works. But I can't see any significant differences between it and my version.
Any suggestions on where to look?
thanks.
EDIT: Yes, I know that this is pretty much an exact duplicate of Printing Off-screen PDFViews but that never got a sufficient answer... (And I didn't notice it until just now...)
Sounds like you have a memory management issue here. Have you checked the console log to see if there's an exception being thrown? How are you creating your PDFView?
But why not do it the way WebKit does it?
Specifically, declare a category on PDFDocument
#interface PDFDocument (PDFSecretsIKnowViaWebKit)
- (NSPrintOperation *)getPrintOperationForPrintInfo:(NSPrintInfo *)printInfo autoRotate:(BOOL)doRotate;
#end
Then when you want to print your PDFDocument simply get an NSPrintOperation from it and run it.
NSPrintOperation *op = [myPDFDoc getPrintOperationForPrintInfo:info autoRotate:YES];
[op runOperation];
// [op runOperationModalForWindow:delegate:didRunSelector:contextInfo:] if you have a window to attach it to
This works for me too. I've verified that getPrintOperationForPrintInfo:autoRotate: exists and appears to work correctly on 10.4, 10.5 and 10.6.