I'm generating a PDF something like:
UIGraphicsBeginPDFContextToFile
layer.render(in: context)
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.
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)
}
}
}
UILabel
I ditched the UILabel and did it directly in the context instead – LockupUIGraphicsPDFRenderer
and callingpdfData
with gets you a block with a context that can be used – Lockup