UILabel text duplicates in PDF when containing a certain character
Asked Answered
L

0

3

I'm generating a PDF something like:

  1. UIGraphicsBeginPDFContextToFile
  2. layer.render(in: context)
  3. UIGraphicsEndPDFContext

Yesterday, I found a fix to make all text vector-based (basically this answer https://mcmap.net/q/493904/-rendering-a-uiview-into-a-pdf-as-vectors-on-an-ipad-sometimes-renders-as-bitmap-sometimes-as-vectors).

That worked really well, text is vectors and searchable, etc.

Except for one irritating thing.

Everytime a UILabel contains the character "Å", the text is kind of duplicated in the PDF, like the image below. Probably there are other characters as well the would cause this. enter image description here

I've got a small example that demonstrates it below. If you run this in the simulator you'll get a pdf in /tmp/test.pdf where you can see the issue yourself.

I guess I should file a rdar, but I would really like some good workaround (good meaning not checking if label.text contains "Å"). Since I don't think this is something Apple would fix, considering the whole workaround to begin with (PDFLabel).

import UIKit

class PDFLabel: UILabel {

    override func draw(_ layer: CALayer, in ctx: CGContext) {
        let isPDF = !UIGraphicsGetPDFContextBounds().isEmpty
        if !layer.shouldRasterize && isPDF {
            draw(bounds)
        } else {
            super.draw(layer, in: ctx)
        }
    }

}

func generatePDFWith(_ texts: [String]) {
    let paper = CGRect(origin: .zero, size: CGSize(width: 876, height: 1239))

    UIGraphicsBeginPDFContextToFile("/tmp/test.pdf", paper, [
        kCGPDFContextCreator as String: "SampleApp"
    ])

    texts.forEach { text in
        UIGraphicsBeginPDFPage()
        let v = UIView()
        let label = PDFLabel()
        label.text = text
        label.textColor = .black

        v.translatesAutoresizingMaskIntoConstraints = false
        label.translatesAutoresizingMaskIntoConstraints = false

        v.addSubview(label)
        v.widthAnchor.constraint(equalToConstant: 500).isActive = true
        v.heightAnchor.constraint(equalToConstant: 500).isActive = true

        label.centerXAnchor.constraint(equalTo: v.centerXAnchor).isActive = true
        label.centerYAnchor.constraint(equalTo: v.centerYAnchor).isActive = true

        v.setNeedsLayout()
        v.layoutIfNeeded()
        v.layer.render(in: UIGraphicsGetCurrentContext()!)
    }

    UIGraphicsEndPDFContext();
    print("Done!")
}

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        generatePDFWith([
            "Ångavallen",
            "Hey Whatsapp?",
            "Änglavallen",
            "Örebro",
            "Råå",
            "RÅÅ",
            "Å",
            "Ål",
        ])
    }

}

EDIT: Little progress in debugging, it seems that the draw function gets called two times, and the first time it is drawn a little "off" if it has an "Å" (probably any character larger than the box).

So this fixed it for me (however ugly it may be):

class PDFLabel: UILabel {

    var count = 0

    override func draw(_ layer: CALayer, in ctx: CGContext) {
        let isPDF = !UIGraphicsGetPDFContextBounds().isEmpty
        if isPDF {
            if count > 0 { draw(bounds) }
            count += 1
        } else if !layer.shouldRasterize {
            draw(bounds)
        } else {
            super.draw(layer, in: ctx)
        }
    }

}
Lockup answered 19/12, 2018 at 11:11 Comment(4)
did you find a better solution?Narbada
@PeterLapisu not really with UILabel I ditched the UILabel and did it directly in the context insteadLockup
hmm ok, feel free than to share maybe your approach as an answer?Narbada
I don't think that's suitable since that has nothing to do with UILabel, which the question concerns and it's not just a matter of a simple change you need to restructure how you render pdfs totally. It involves creating a UIGraphicsPDFRenderer and calling pdfData with gets you a block with a context that can be usedLockup

© 2022 - 2024 — McMap. All rights reserved.