How to AirPrint multiple print formatters in one go?
Asked Answered
D

1

16

My ultimate goal is to create multiple UIMarkupTextPrintFormatter and print them all in one print job.

For example, the first formatter contains text that fills up the first page and a little bit of the second page. Although there is still space left to print the second formatter, I want the second formatter to print on the third page. So basically, before printing another formatter, start a new page.

I cannot know how many pages one formatter will fill up at compile time because it depends on user input. Maybe 1, 2 or more.

I heard that I need to use UIPrintPageRenderer so I read its docs and tried this:

// I'm using webviews here because I don't want to write 2 pages worth of markup...
// it should be the same anyway. The two webviews display different wikipedia articles
// Also, don't judge my variable names. I'm lazy and this is just test code...
let formatter = webView.viewPrintFormatter()
let formatter1 = webView2.viewPrintFormatter()

formatter.contentInsets = UIEdgeInsets(top: 72, left: 72, bottom: 72, right: 72)
formatter1.contentInsets = UIEdgeInsets(top: 72, left: 72, bottom: 72, right: 72)

let renderer = UIPrintPageRenderer()
renderer.addPrintFormatter(formatter, startingAtPageAt: 0)
renderer.addPrintFormatter(formatter1, startingAtPageAt: formatter.pageCount)

printController.printPageRenderer = renderer
printController.present(animated: true, completionHandler: nil)

To add the second print formatter, I used formatter.pageCount as the second argument. I hoped it will add the second formatter after the last page of the first. But it didn't. It only printed the stuff in the second formatter.

I printed formatter.pageCount and found its value is 0.

I am totally confused. How can I get how many pages a formatter will fill up?

I also tried to implement my own renderer:

class MyRenderer: UIPrintPageRenderer {
    override func drawPrintFormatter(_ printFormatter: UIPrintFormatter, forPageAt pageIndex: Int) {
        printFormatter.draw(in: self.printableRect, forPageAt: pageIndex)
    }
}

But then it just crashes when the print interaction controller showed:

Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread. Crashing now...

What did I do wrong in my code?

Daggett answered 6/12, 2016 at 8:3 Comment(5)
You are calling UI stuff from a thread that is not the main thread. You must call UI stuff only from the main thread. Show where you call this code. Probably from within the completion handler of a download task or a delegate of a the webview.Lagos
@Lagos I am doing this in an @IBAction method, which should be on the UI thread.Daggett
Without seeing the real code, it is hard to impossible to help.Lagos
@Lagos so you can't reproduce the problem with the given code? Does my code work on your machine?Daggett
I am afraid without more real code, this (reproducing) is impossible.Lagos
C
2

As per doc it seems, UIPrintFormatter need all the information before it calculates number of pages to print in the format.

UIPrintFormatter publishes an interface that allows you to specify the starting page for a print job and the margins around the printed content; given that information plus the content, a print formatter computes the number of pages for the print job.

So this will not happen unless UIPrintFormatter start printing the content.

Since UIPrintPageRenderer may use UIPrintFormatter it can actually get number of pages associated with formatter and all you have to do is to override numberOfPages to setup different formatter at each page.

Below is the sample code from Apple. Which does exactly same thing - attaching multiple formatter (UIMarkupTextPrintFormatter) at each pages.

It overrides numberOfPages here:

- (NSInteger)numberOfPages {
    self.printFormatters = nil;
    [self setupPrintFormatters];
    return [super numberOfPages];
}

And attach formatter to each page here:

  /*
     Iterate through the recipes setting each of their html representations into 
     a UIMarkupTextPrintFormatter and add that formatter to the printing job.
     */
    - (void)setupPrintFormatters {
        NSInteger page = 0;
        CGFloat previousFormatterMaxY = CGRectGetMinY(self.contentArea);

        for (Recipe *recipe in recipes) {
            NSString *html = recipe.htmlRepresentation;

            UIMarkupTextPrintFormatter *formatter = [[UIMarkupTextPrintFormatter alloc] initWithMarkupText:html];
            [formatterToRecipeMap setObject:recipe forKey:formatter];

            // Make room for the recipe info
            UIEdgeInsets contentInsets = UIEdgeInsetsZero;
            contentInsets.top = previousFormatterMaxY + self.recipeInfoHeight;
            if (contentInsets.top > CGRectGetMaxY(self.contentArea)) {
                // Move to the next page
                page++;
                contentInsets.top = CGRectGetMinY(self.contentArea) + self.recipeInfoHeight;
            }
            formatter.contentInsets = contentInsets;

            // Add the formatter to the renderer at the specified page
            [self addPrintFormatter:formatter startingAtPageAtIndex:page];

            page = formatter.startPage + formatter.pageCount - 1;
            previousFormatterMaxY = CGRectGetMaxY([formatter rectForPageAtIndex:page]);

        }
    }

In short it is calculating page number based on content area, content inset and then attaching formatter to the page by calling the API:

 [self addPrintFormatter:formatter startingAtPageAtIndex:page];

This way Renderer has information about formatter for each pages. You can find complete code here

Hope it will help.

Camara answered 26/9, 2017 at 5:15 Comment(4)
Will this start a new page for each print formatted?Daggett
Well, it does if we check you see the line page = formatter.startPage + formatter.pageCount - 1; In this line it actually calculates how many pages a formatter will held up. and then again in next cycle in for loop it increase the page count.Camara
I see. Thank you! I will try this out.Daggett
I tried it out and it still says I am on the wrong thread. I think the problem is not because I am using multiple formatters. It seems to crash even when I use one single simple text formatter. Therefore, I think I will ask a new question.Daggett

© 2022 - 2024 — McMap. All rights reserved.