3

Has anyone tried using multiple formatters (UIViewPrintFormatter, UIMarkupTextPrintFormatter, UISimpleTextPrintFormatter) with a page renderer (UIPrintPageRenderer) to print the content?

I'm trying to use two UIMarkupTextPrintFormatters with a UIPrintPageRenderer subclass, but i'm failing to get the print. I'm using MyPrintPageRenderer class from PrintWebView sample code.

I've gone through the Apple's documentation but it isn't very helpful, and there is no sample code associated with the description. I've tried a couple of solutions but so far I haven't had any success.

Any suggestions?

Cliff Ribaudo
  • 8,932
  • 2
  • 55
  • 78
Mustafa
  • 20,504
  • 42
  • 146
  • 209

3 Answers3

6

The fact that there is very little activity in the "Printing" section suggests that either not many people are using this, or people using this API are not visiting this community, or people are just ignoring the question for some reason.

Anyways, i was able to solve my problem. I was using setPrintFormatters: method earlier which wasn't/isn't working. I don't know why. So, i started experimenting with addPrintFormatter:startingAtPageAtIndex: method instead. And here's how i solved my problem:

// To draw the content of each page, a UIMarkupTextPrintFormatter is used.
NSString *htmlString = [self prepareWebViewHTML];
UIMarkupTextPrintFormatter *htmlFormatter = [[UIMarkupTextPrintFormatter alloc] initWithMarkupText:htmlString];

NSString *listHtmlString = [self prepareWebViewListHTMLWithCSS];
UIMarkupTextPrintFormatter *listHtmlFormatter = [[UIMarkupTextPrintFormatter alloc] initWithMarkupText:listHtmlString];

// I think this should work, but it doesn't! The result is an empty page with just the header and footer text.
// [myRenderer setPrintFormatters:[NSArray arrayWithObjects:htmlFormatter, listHtmlFormatter, nil]];

// Alternatively, i've used addPrintFormatters here, and they just work!
// Note: See MyPrintPageRenderer's numberOfPages method implementation for relavent details.
// The important point to note there is that the startPage property is updated/corrected.
[myRenderer addPrintFormatter:htmlFormatter startingAtPageAtIndex:0];
[myRenderer addPrintFormatter:listHtmlFormatter startingAtPageAtIndex:1];

In the MyPrintPageRenderer, i used following code to update/correct the startPage property so that a new page is used for each formatter:

- (NSInteger)numberOfPages
{
    // TODO: Perform header footer calculations
    // . . .

    NSUInteger startPage = 0;
    for (id f in self.printFormatters) {
        UIPrintFormatter *myFormatter = (UIPrintFormatter *)f;

        // Top inset is only used if we want a different inset for the first page and we don't.
        // The bottom inset is never used by a viewFormatter.
        myFormatter.contentInsets = UIEdgeInsetsMake(0, leftInset, 0, rightInset);

        // Just to be sure, never allow the content to go past our minimum margins for the content area.
        myFormatter.maximumContentWidth = self.paperRect.size.width - 2*MIN_MARGIN;
        myFormatter.maximumContentHeight = self.paperRect.size.height - 2*MIN_MARGIN;

        myFormatter.startPage = startPage;
        startPage = myFormatter.startPage + myFormatter.pageCount;
    }

    // Let the superclass calculate the total number of pages
    return [super numberOfPages];
}

I still don't know if there is anyway to APPEND the printable content of both htmlFormatter and listHtmlFormatter (using this approach). e.g. rather than using a new page for listHtmlFormatter, continue printing from where htmlFormatter ended.

Mustafa
  • 20,504
  • 42
  • 146
  • 209
  • @Mustafa: Sorry, Mustafa. I'm trying to print the content of a uitextview, but the printer simulator prints it all in one line. If I replace "\n" with "\n\n", the result isn't always the same on the printer simulator. Did you have similar problems? Moreover, I would to print some images after the content of the uitextview. What could I use? UIPrintPageRenderer? Any suggestion is welcomed. I didn't found simple example code well explained to start with. Thanks in advance. – Sefran2 May 31 '11 at 08:46
  • 2
    @Fran, No, i didn't encounter the print issue you've mentioned. You can use the code above (that uses UIPrintPageRenderer) to get the desired results. Create UISimpleTextPrintFormatter object for your plain text and similarly create formatters for your images, and use a page renderer to print them all. Or, you can create an html file using plain text and images, and use UIMarkupTextPrintFormatter with page renderer for printing. – Mustafa May 31 '11 at 12:07
  • 1
    I have a sample project which i created for testing html content (including images). I can share that with you if that can help. – Mustafa May 31 '11 at 12:09
  • @Mustafa: Thanks for the replay. I tried to create an html string with my uitextview content and images. As concerns the newline issue, I resolved replacing "\n" with
    . I also succeed in displaying the images. How did you set width and height for the displayed images? I create an UIImage from my image file, because I would to use image.size.width and image.size.height, but there is a difference between ios 4 and superior and ios 3.1 (I support all) as concern the size property.
    – Sefran2 May 31 '11 at 12:23
  • 1
    I used the original size of the images. I don't understand your issue with image size though. Are you referring to the scale property of UIImage? – Mustafa May 31 '11 at 13:56
  • @Mustafa: In the docs, I read for the size property of an uiimage "In iOS 4.0 and later, this value reflects the logical size of the image and is measured in points. In iOS 3.x and earlier, this value always reflects the dimensions of the image measured in pixels". This is the link: http://developer.apple.com/library/ios/#documentation/uikit/reference/UIImage_Class/Reference/Reference.html. When I build the htmlstring, I use image.size.width and image.size.height, but it doesn't work, so I don't know if the problem is that they don't return pixel but points. – Sefran2 May 31 '11 at 14:19
  • 1
    How do you build the html string? image.size should return the image size, provided it contains a valid image reference. The size can be different then what you might expect, but still it should be a valid CGSize. I might be able to help if you can post the piece of code that's not working for you. – Mustafa May 31 '11 at 15:38
  • @Mustafa: NSMutableString *htmlString = [NSMutableString stringWithString:@"iLog note"]; [htmlString appendString:@""]; [htmlString appendString:@"

    "]; where image is the uiimage that I create from absolutePath. The image is retrieved correctly.
    – Sefran2 May 31 '11 at 16:17
  • @Mustafa: I think the problem is how I save the images. The user images are all scaled at 640x960, and I think that the scaling is applied also to imgs smaller than 640x960, resulting in a bigger imgs than the original ones. I have to do some tries with 640x960. – Sefran2 May 31 '11 at 16:26
  • @Mustafa: Sorry, again. Did you ever use a custom UIPrintPageRenderer subclass to print a footer and header on each page? I have an object with contains some informations that I have to print, some of this info will be used for the header and the other ones for the content, but I don't know if I can create a complete html string for the header and another one for the content, passing them to the custom page renderer. – Sefran2 Jun 01 '11 at 16:30
  • 1
    You don't really need to specify the image height and width with tag. It can use the default image size, but if you want to use a resizable image, you can try to create HTML with resized UIImage represented as base64 encoded NSData (and instead of "file://..." use [NSString stringWithFormat:@"data:image/jpeg;base64,%@", [imageData base64EncodedString]]). e.g. Re custom UIPrintPageRenderer subclass, yes i'm using it, but in my case the content in header and footer is not html based. You can use one of the Apple's sample codes to check how to add header and footer. – Mustafa Jun 02 '11 at 11:27
  • @Mustafa: Thank you very much. I succeeded in adding an header and footer (simple strings however). I'll try to use your suggestion about the resized uiimage. – Sefran2 Jun 03 '11 at 10:33
  • OMFG!!!! Dude, you saved me. I have been dk'n around with this nonsense for 2 days trying to figure out why I couldn't successfully iterate over. Over the list of attached formatters and get it to work. I also noticed that it worked when I added them the way you suggested but not as an array. I didn't think of this. Nice! I upvoted everything you wrote that I could in this post. – Cliff Ribaudo Mar 02 '16 at 21:36
2

I'm adding this extra info answer here because I spent 2 days dk'n around with this and Mustafa's answer saved me. Hopefully all this in one place will save others a lot of wasted time.

I was trying to figure out why I was unable get my custom UIPrintPageRenderer to iterate over an array of viewPrintFormatter from several UIWebViews that I need to print in ONE job, and have it properly calculate pageCount as these Apple docs suggest it should: PrintWebView

The key was to add them via this method:

-(void)addPrintFormatter:(UIPrintFormatter *)formatter startingAtPageAtIndex:(NSInteger)pageIndex

and NOT this one:

@property(nonatomic, copy) NSArray <UIPrintFormatter *> *printFormatters

The Apple docs suggest that the UIPrintPageRenderer gets page count from the print formatters in the array as long as the proper metrics are set on the print formatters, as Mustafa shows above. But it only works if you add them via addPrintFormatter:startingAtPageAtIndex:

If you add them as an array, the printFormatters (regardless of what metrics you set on them) will return a page count of 0!

One additional note for people using this approach, put the update to viewPrintFormatter.startPage in a dispatch_async block, else you'll get an exception:

dispatch_async(dispatch_get_main_queue(), ^{
    myFormatter.startPage = startPage;
    startPage = myFormatter.startPage + myFormatter.pageCount;
});
Mustafa
  • 20,504
  • 42
  • 146
  • 209
Cliff Ribaudo
  • 8,932
  • 2
  • 55
  • 78
0

To add to @Mustafa's answer, startPage needs to be declared as:

__block NSUInteger startPage

if it's to be used inside the dispatch_async block

iRon111
  • 111
  • 5