Last Updated: August 06, 2016
· xjki

Rendering html pdf report for printing from UIWebView

HTML in UIWebView on iOS is rendered at 72dpi - it's fine for displaying on screen but when you generate .pdf file for printing using UIGraphicsBeginPDFContextToData, the end result will be .pdf file with 72dpi and it will not look nice when printed (heavily pixelized).

The trick is to :
1) scale everything in your HTML report 4 times bigger (I do this by css selctor sizes beeing defined as % of body size which is only parameter passed to report) and load it in UIWebView. See example of report css/html

2) create pdf by rendering UIWebView using 2480x3540 page (A4 in 300dpi)

3) in renderer scale down end result

Use of renderer (save NSData in pdf file).

private func printablePdfData(webView: UIWebView) -> NSData {
   let A4Size = CGSizeMake(2480, 3504) // A4 in pixels at 300dpi
   let renderer = PRV300dpiPrintRenderer()
   let formatter = webView.viewPrintFormatter()
   formatter.perPageContentInsets = UIEdgeInsetsZero
   renderer.addPrintFormatter(formatter, startingAtPageAtIndex: 0)
   let topPadding: CGFloat = 115.0
   let bottomPadding: CGFloat = 117.0
   let leftPadding: CGFloat = 100.0
   let rightPadding: CGFloat = 100.0
   let printableRect = CGRectMake(leftPadding, topPadding, A4Size.width - leftPadding - rightPadding, A4Size.height - topPadding - bottomPadding)
   let A4Rect = CGRectMake(0, 0, A4Size.width, A4Size.height)
   renderer.setValue(NSValue.init(CGRect: A4Rect), forKey: "paperRect")
   renderer.setValue(NSValue.init(CGRect: printableRect), forKey: "printableRect")
   let data = renderer.pdfData()
   return data

Renderer class itself

class PRV300dpiPrintRenderer : UIPrintPageRenderer {
   func pdfData() -> NSData {
      let data = NSMutableData()
      UIGraphicsBeginPDFContextToData(data, self.paperRect, nil)
      let pdfContext = UIGraphicsGetCurrentContext()
      CGContextConcatCTM(pdfContext,CGAffineTransformMakeScale(72/300, 72/300)) // scale down to improve dpi from screen 72dpi to printable 300dpi
      let bounds = UIGraphicsGetPDFContextBounds()
      for i in 0..<self.numberOfPages() {
         self.drawPageAtIndex(i, inRect: bounds)
      return data