ios- pdf generation in uiscrollview - objective-c

in my ios app i am listing out some data in scroll view. When a button is clicked the datas in the page are been generated in pdf file format. Following is the bit of code.
- (void)createPDFfromUIView:(UIView*)aView saveToDocumentsWithFileName:(NSString*)aFilename{
// Creates a mutable data object for updating with binary data, like a byte array
NSMutableData *pdfData = [NSMutableData data];
// Points the pdf converter to the mutable data object and to the UIView to be converted
UIGraphicsBeginPDFContextToData(pdfData, aView.bounds, nil);
UIGraphicsBeginPDFPage();
CGContextRef pdfContext = UIGraphicsGetCurrentContext();
// draws rect to the view and thus this is captured by UIGraphicsBeginPDFContextToData
[aView.layer renderInContext:pdfContext];
// remove PDF rendering context
UIGraphicsEndPDFContext();
// Retrieves the document directories from the iOS device
NSArray* documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);
NSString* documentDirectory = [documentDirectories objectAtIndex:0];
NSString* documentDirectoryFilename = [documentDirectory stringByAppendingPathComponent:aFilename];
// instructs the mutable data object to write its context to a file on disk
[pdfData writeToFile:documentDirectoryFilename atomically:YES];
NSLog(#"documentDirectoryFileName: %#",documentDirectoryFilename);
}
From the above code, i am able to generate a pdf file, but i am facing the following issue.
If i have more than 50 data, in my app i am able to see only the first 10 data and to see the others i need to scroll them. In this case when the pdf file gets generated it is taking only the data of first 10 and not the others. How to get the other data too

renderInContext: will only render what you currently see - UIScrollView is smart and will only have the views in the hierarchy that you need right now. You need a for-loop to manually scroll and redraw. This might be tricky to get right, maybe call [aView layoutSubviews] manually after scrolling to force the scrollView to rearrange the subviews instantly.

as steipete said, you can use loop to renderIncontext
- You need to calculate num of loop
numOfLoop = webview.ScrollView.ContentSize.Height/weview.frame.size.height
use loop to renderInContext
for(int i = 0; i < numOfLopp; i ++)
1.UIGraphicsBeginPDFPage()
get current context
renderIncontext
This way run correct in my app but I am getting with BIG issue when the numOfLoop lager
- you will get slow to renderInContext
- Memory will be increase -> Close app
In this way, How can I remove/release context when I am in once loop ?
Other way, you can use https://github.com/iclems/iOS-htmltopdf/blob/master/NDHTMLtoPDF.h
to generate to PDF

#steipete, #all Hi i tried the below code it generate multipage PDF, but the visible area in view is repeated for all the pages. please add any further ideas to improve the result.
-(void)createPDFfromUIView:(UIWebView*)aView saveToDocumentsWithFileName:(NSString*)aFilename
{
NSString *oldFilePath= [[NSBundle mainBundle] pathForResource:#"ABC" ofType:#"pdf"];
NSMutableData *pdfData = [NSMutableData data];
UIGraphicsBeginPDFContextToData(pdfData, aView.bounds, nil);
CFURLRef url = CFURLCreateWithFileSystemPath (NULL, (CFStringRef)oldFilePath, kCFURLPOSIXPathStyle, 0);
CGPDFDocumentRef templateDocument = CGPDFDocumentCreateWithURL(url);
CFRelease(url); size_t count = CGPDFDocumentGetNumberOfPages(templateDocument);
for (size_t pageNumber = 1; pageNumber <= count; pageNumber++)
{
UIGraphicsBeginPDFPage();
CGContextRef pdfContext = UIGraphicsGetCurrentContext();
[aView.layer renderInContext:pdfContext];
}
CGPDFDocumentRelease(templateDocument);
// remove PDF rendering context UIGraphicsEndPDFContext();
// Retrieves the document directories from the iOS device
NSArray* documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
NSString* documentDirectory = [documentDirectories objectAtIndex:0];
NSString* documentDirectoryFilename = [documentDirectory stringByAppendingPathComponent:aFilename];
[pdfData writeToFile:documentDirectoryFilename atomically:YES];
NSLog(#"documentDirectoryFileName: %#",documentDirectoryFilename);
}

Check ScrollViewToPDF example
It uses same scrollview's layer renderInContext but here PDF is created according to your requirement such as one page PDF or multiple page PDF
Note : It captures all visible as well as invisible part of scrollView

Related

Memory problems while converting cvMat to UIImage

before posting this question here, i have read all the materials and similar posts on it but i cant get the main "idea" what is happening and how to fix it, in 10 of the similar question, everyone was fixing this problem with #autoreleasepool in this case i was unable to achive my goal. So while converting cvMat to UIImage i have increasing memory depending on size.
Below are step which i am doing before converting mat to uiimage:
cv::Mat undistorted = cv::Mat(cvSize(maxWidth,maxHeight), CV_8UC1);
cv::Mat original = [MatStructure convertUIImageToMat:adjustedImage];
cv::warpPerspective(original, undistorted, cv::getPerspectiveTransform(src, dst), cvSize(maxWidth, maxHeight));
original.release();
adjustedImage = [MatStructure convertMatToUIImage:undistorted];
undistorted.release();
problem is visible while i am converting my mat to uiimage, memory goes up to 400 mb and on every cycle it rises.
+ (UIImage *) convertMatToUIImage: (cv::Mat) cvMat {
NSData *data = [NSData dataWithBytes:cvMat.data length:cvMat.elemSize() * cvMat.total()];
CGColorSpaceRef colorSpace;
if (cvMat.elemSize() == 1) {
colorSpace = CGColorSpaceCreateDeviceGray();
} else {
colorSpace = CGColorSpaceCreateDeviceRGB();
}
CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef) data);
CGBitmapInfo bmInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big;
CGImageRef imageRef = CGImageCreate(cvMat.cols, // width
cvMat.rows, // height
8, // bits per component
8 * cvMat.elemSize(), // bits per pixel
cvMat.step.p[0], // bytesPerRow
colorSpace, // colorspace
bmInfo, // bitmap info
provider, // CGDataProviderRef
NULL, // decode
false, // should interpolate
kCGRenderingIntentDefault // intent
);
UIImage *image = [[UIImage alloc] initWithCGImage:imageRef];
CGImageRelease(imageRef);
CGDataProviderRelease(provider);
CGColorSpaceRelease(colorSpace);
cvMat.release(); // this line is optional.
return image;
}
I have seen many similar code but every single example works as this one.
I belive that problem holds in (__bridge CFDataRef) and ARC cant clean up this data, if i will try to CFRelease((__bridge CFDataRef)data) than will happen crash because program will search for allocated memory and it will be freed already so it will run to crash.
I am using openCV3 and have tried their method MatToUIImage but problem still exsits, on leaks profiler there are no leaks at all, and most expensive task in memory is convertMatToUIImage.
I am reading all day about it but actually can't find any useful solution yet.
Currently i work on swift 3.0 which inherits class XXX and it uses objC class to crop something and than return to UIImage as well. In deinit i am assigning this inherited class property nil, but problem still exsists.Also i think that dataWithBytes is duplicating memory like if i have 16MB at start after creating NSData it will be 32MB..
And please if you can suggests useful threads about this problem i will be glad to read them all. Thanks for help
After working on this problem more than three days, i had to rewrite function and it worked 100%, i have tested on five different devices.
CFRelease, Free() and #autoreleasepool did not helped me at all and i implemented this:
data = UIImageJPEGRepresentation([[UIImage alloc] initWithCGImage:imageRef], 0.2f); // because images are 30MB and up
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *appFile = [documentsDirectory stringByAppendingPathComponent:#"MyFile.jpeg"];
[data writeToFile:appFile atomically:NO];
data = nil;
after this solution everything worked fine. So i grab the UIImage and converting to NSData, after that we should save it to the local directory and the only thing left is to read the data from directory. hope this thread will help someone one day.

How to allow forms(pdf) to be re-editable after they have been saved(Objective C)

I'm new to CoreGraphics framework.
We have used ILPDFKit library to render PDF or form
We embedded the drawn paths to existing PDF.Here is the code
-(NSData *)embededPdfAnnotationPointsInPdfAtPath:(NSString *)pdfPath
{
NSURL *pdfUrl = [NSURL fileURLWithPath:pdfPath];
CGPDFDocumentRef pdf = CGPDFDocumentCreateWithURL((CFURLRef)pdfUrl);
NSMutableData *data = [NSMutableData new];
UIGraphicsBeginPDFContextToData(data, CGRectZero, nil);
for(NSUInteger pageIndex = 1; pageIndex <= [self pdfDrawViewInfo].count; pageIndex ++)
{
// Get the current page and page frame
CGPDFPageRef pdfPage = CGPDFDocumentGetPage(pdf, pageIndex);
const CGRect pageFrame = CGPDFPageGetBoxRect(pdfPage, kCGPDFMediaBox);
UIGraphicsBeginPDFPageWithInfo(pageFrame, nil);
// Draw the page (flipped)
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSaveGState(ctx);
CGContextScaleCTM(ctx, 1, -1);
CGContextTranslateCTM(ctx, 0, -pageFrame.size.height);
CGContextDrawPDFPage(ctx, pdfPage);
CGContextRestoreGState(ctx);
UIImage *drawViewImage = [self annotatedImageForPdfPageAtIndex:(pageIndex - 1)];
UIImage *annotatedImgForPdfPage = [self imageWithImage:drawViewImage scaledToSize:pageFrame.size] ;
[annotatedImgForPdfPage drawInRect:pageFrame];
}
UIGraphicsEndPDFContext();
// CGPDFDocumentRelease(pdf);
// CGImageRelease(annotatedImgCGref);
return data;
}
Above code indicates, we are just pasting image(drawn paths) on existing PDF.
Later functionality changed as "After saving PDF with annotations(freehand drawing), we need to have control over annotated stuff"
Question:
Edit PDF with annotation(freehand drawing), save it.After saving the PDF with annotated stuff, we need to again gain its editing capability. We need to know the saving mechanism and steps of this process.Please put your views here, so that it help me a lot.
Thanks inadvance
I would probably conserve the pdf original version, and store the edits in another file.
Than each time the user save, the app apply the annotations and other stuff to original version and generate the final pdf (in case of sharing or exportation features)
That would give you a complete control, (i guess this is also the same way "apple photo", "iPhoto" and "Aperture" manage the pictures modifications...)

Saving NSBitmapImageRep as image

I've done an exhaustive search on this and I know similar questions have been posted before about NSBitmapImageRep, but none of them seem specific to what I'm trying to do which is simply:
Read in an image from the desktop (but NOT display it)
Create an NSBitmap representation of that image
Iterate through the pixels to change some colours
Save the modified bitmap representation as a separate file
Since I've never worked with bitmaps before I thought I'd just try to create and save one first, and worry about modifying pixels later. That seemed really straightforward, but I just can't get it to work. Apart from the file saving aspect, most of the code is borrowed from another answer found on StackOverflow and shown below:
-(void)processBitmapImage:(NSString*)aFilepath
{
NSImage *theImage = [[NSImage alloc] initWithContentsOfFile:aFilepath];
if (theImage)
{
CGImageRef CGImage = [theImage CGImageForProposedRect:nil context:nil hints:nil];
NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithCGImage:CGImage];
NSInteger width = [imageRep pixelsWide];
NSInteger height = [imageRep pixelsHigh];
long rowBytes = [imageRep bytesPerRow];
// above matches the original size indicating NSBitmapImageRep was created successfully
printf("WIDE pix = %ld\n", width);
printf("HIGH pix = %ld\n", height);
printf("Row bytes = %ld\n", rowBytes);
// We'll worry about this part later...
/*
unsigned char* pixels = [imageRep bitmapData];
int row, col;
for (row=0; row < height; row++)
{
// etc ...
for (col=0; col < width; col++)
{
// etc...
}
}
*/
// So, let's see if we can just SAVE the (unmodified) bitmap first ...
NSData *pngData = [imageRep representationUsingType: NSPNGFileType properties: nil];
NSString *destinationStr = [self pathForDataFile];
BOOL returnVal = [pngData writeToFile:destinationStr atomically: NO];
NSLog(#"did we succeed?:%#", (returnVal ? #"YES": #"NO")); // the writeToFile call FAILS!
[imageRep release];
}
[theImage release];
}
While I like this code for its simplicity, another potential issue down the road might be that Apple docs advise us treat bitmaps returned with 'initWithCGImage' as read-only objects…
Can anyone please tell me where I'm going wrong with this code, and how I could modify it to work. While the overall concept looks okay to my non-expert eye, I suspect I'm making a dumb mistake and overlooking something quite basic. Thanks in advance :-)
That's a fairly roundabout way to create the NSBitmapImageRep. Try creating it like this:
NSBitmapImageRep* imageRep = [NSBitmapImageRep imageRepWithContentsOfFile:aFilepath];
Of course, the above does not give you ownership of the image rep object, so don't release it at the end.

Writing image metadata (EXIF/TIFF/IPTC) to image file in OS X

I am creating a photo editing app, and so far I've managed to read the metadata from image files successfully (after getting an answer to this question: Reading Camera data from EXIF while opening NSImage on OS X).
source = CGImageSourceCreateWithURL((__bridge CFURLRef)url, NULL);
NSDictionary *props = (__bridge_transfer NSDictionary *) CGImageSourceCopyPropertiesAtIndex(source, 0, NULL);
This copies all the metadata of the image file to a dictionary, and it works faily well. However, I couldn't find out how to write this metadata back to a newly created NSImage (or to an image file). Here is how I save my file (where img is an NSImage instance without metadata and self.cachedMetadata is the dictionary read from the initial image):
NSBitmapImageRep *rep = [NSBitmapImageRep imageRepWithData:[img TIFFRepresentation]];
[rep setProperty:NSImageEXIFData withValue:self.cachedMetadata];
NSData *data;
if([[fileName lowercaseString] rangeOfString:#".png"].location != NSNotFound){
data = [rep representationUsingType:NSPNGFileType properties:nil];
}else if([[fileName lowercaseString] rangeOfString:#".tif"].location != NSNotFound){
data = [rep representationUsingType:NSTIFFFileType properties:nil];
}else{ //assume jpeg
data = [rep representationUsingType:NSJPEGFileType properties:#{NSImageCompressionFactor: [NSNumber numberWithFloat:1], NSImageEXIFData: self.cachedMetadata}];
}
[data writeToFile:fileName atomically:YES];
How can I write the metadata? I used to write just EXIF for JPEG (the dictionary was EXIF-only previously) successfully but because EXIF lacked some of the fields that the initial images had (IPTC and TIFF tags) I needed to change my reading method. Now I have all the data, but I don't know how to write it to the newly-created image file.
Thanks,
Can.
Found an answer from another StackOverflow question: How do you overwrite image metadata?:
(code taken from that question itself and modified for my needs, which contains the answer to my question)
//assuming I've already loaded the image source and read the meta
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef) data, NULL);
CFStringRef imageType = CGImageSourceGetType(source);
//new empty data to write the final image data to
NSMutableData *resultData = [NSMutableData data];
CGImageDestinationRef imgDest = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)(resultData), imageType, 1, NULL);
//copy image data
CGImageDestinationAddImageFromSource(imgDest, source, 0, (__bridge CFDictionaryRef)(window.cachedMetadata));
BOOL success = CGImageDestinationFinalize(imgDest);
This worked perfectly for writing back all the metadata including EXIF, TIFF, and IPTC.
You could try either using, or looking at the code in, the "exiv2" tool maybe.

iOS - Creating multi page PDF from HTML content [closed]

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
I have a long html page and wanted to convert it in to a multipage PDF file.
I have followed the instructions provided in
apple and here - how to make multi page PDF for a given string content.
But the formatting the NSString ( with some like data ) is difficult than creating a html page. I have created this html and displaying it in a UIWebView.
Now I want to create a PDF out of this HTML to a Multi page PDF file.
The code I'm using can create single PDF.
- (void)createPDFfromUIView:(UIWebView *)aView saveToDocumentsWithFileName:(NSString*)aFilename
{
// Creates a mutable data object for updating with binary data, like a byte array
NSMutableData *pdfData = [NSMutableData data];
// Points the pdf converter to the mutable data object and to the UIView to be converted
UIGraphicsBeginPDFContextToData(pdfData, aView.bounds, nil);
UIGraphicsBeginPDFPage();
CGContextRef pdfContext = UIGraphicsGetCurrentContext();
// draws rect to the view and thus this is captured by UIGraphicsBeginPDFContextToData
[aView.layer renderInContext:pdfContext];
// remove PDF rendering context
UIGraphicsEndPDFContext();
// Retrieves the document directories from the iOS device
NSArray* documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);
NSString* documentDirectory = [documentDirectories objectAtIndex:0];
NSString* documentDirectoryFilename = [documentDirectory stringByAppendingPathComponent:aFilename];
// instructs the mutable data object to write its context to a file on disk
[pdfData writeToFile:documentDirectoryFilename atomically:YES];
NSLog(#"documentDirectoryFileName: %#",documentDirectoryFilename);
}
Can any one give some help ?
I created a class based on every good advice I found around. I've been digging a lot and I hope my class will offer some good start for anyone trying to create multi-page PDF directly out of some HTML source.
You'll find the whole code here with some basic sample code : https://github.com/iclems/iOS-htmltopdf
I had just the same issue as you and my requirements were:
- full PDF (real text, no bitmap)
- smart multi-pages (compared to cutting a full height webview every X pixels...)
Thus, the solution I use is pretty nice as it resorts to the same tools iOS uses to split pages for print.
Let me explain, I setup a UIPrintPageRenderer based on the web view print formatter (first tip) :
UIPrintPageRenderer *render = [[UIPrintPageRenderer alloc] init];
[render addPrintFormatter:webView.viewPrintFormatter startingAtPageAtIndex:0];
CGRect printableRect = CGRectMake(self.pageMargins.left,
self.pageMargins.top,
self.pageSize.width - self.pageMargins.left - self.pageMargins.right,
self.pageSize.height - self.pageMargins.top - self.pageMargins.bottom);
CGRect paperRect = CGRectMake(0, 0, self.pageSize.width, self.pageSize.height);
[render setValue:[NSValue valueWithCGRect:paperRect] forKey:#"paperRect"];
[render setValue:[NSValue valueWithCGRect:printableRect] forKey:#"printableRect"];
NSData *pdfData = [render printToPDF];
[pdfData writeToFile: self.PDFpath atomically: YES];
In the meantime, I have created a category on UIPrintPageRenderer to support:
-(NSData*) printToPDF
{
[self doNotRasterizeSubviews:self.view];
NSMutableData *pdfData = [NSMutableData data];
UIGraphicsBeginPDFContextToData( pdfData, CGRectZero, nil );
[self prepareForDrawingPages: NSMakeRange(0, self.numberOfPages)];
CGRect bounds = UIGraphicsGetPDFContextBounds();
for ( int i = 0 ; i < self.numberOfPages ; i++ )
{
UIGraphicsBeginPDFPage();
[self drawPageAtIndex: i inRect: bounds];
}
UIGraphicsEndPDFContext();
return pdfData;
}