Dear all, I have an app in which I'm trying to create a pdf from the data stored. Everything is working fine except the fact that the text is not following the page margins, going out of the page of being truncated without being written on the line below.
Has anybody experienced the same in the past?
Please assist, I'm getting out of my mind with this in the last two days... Here the code of the function which is creating the PDF.
let pdfMetaData = [
kCGPDFContextCreator: "Your App Name",
kCGPDFContextAuthor: "Your Name",
kCGPDFContextTitle: "Allenamento"
]
let pageWidth = 8.5 * 72.0 // Standard letter size in points (8.5 x 11 inches)
let pageHeight = 11 * 72.0
let pageSize = CGRect(x: 0, y: 0, width: pageWidth, height: pageHeight)
let margin: CGFloat = 20.0
let pdfData = NSMutableData()
guard let consumer = CGDataConsumer(data: pdfData as CFMutableData) else {
print("Errore nella creazione del consumer")
return
}
var mediaBox = pageSize
guard let pdfContext = CGContext(consumer: consumer, mediaBox: &mediaBox, pdfMetaData as CFDictionary) else {
print("Errore nella creazione del contesto PDF")
return
}
pdfContext.beginPDFPage(nil)
// Parte superiore della pagina
...
// Disegno del rettangolo "MATERIALE DA ALLENAMENTO"
...
// Disegno del rettangolo "ESERCITAZIONI ALLENAMENTO"
let exercisesRect = CGRect(x: margin, y: cellYPosition - 45, width: pageWidth - 2 * margin, height: 25)
pdfContext.setFillColor(NSColor.lightGray.cgColor)
pdfContext.fill(exercisesRect)
pdfContext.setStrokeColor(NSColor.black.cgColor)
pdfContext.stroke(exercisesRect)
let exercisesText = "ESERCITAZIONI ALLENAMENTO"
let exercisesAttributedString = NSAttributedString(string: exercisesText, attributes: boldAttributes)
let exercisesTextPosition = CGPoint(x: exercisesRect.midX - exercisesAttributedString.size().width / 2, y: exercisesRect.midY - 5)
drawText(attributedString: exercisesAttributedString, position: exercisesTextPosition, context: pdfContext)
// Iniziamo a disegnare le informazioni degli esercizi
let sortedExercises = training.trainingExercises.sorted { $0.order < $1.order }
var currentY = exercisesRect.minY - 30
for trainingExercise in sortedExercises {
// Gestione del wrap text e posizionamento su più pagine
let remainingHeight = currentY - margin
if remainingHeight < 100 {
pdfContext.endPDFPage()
pdfContext.beginPDFPage(nil)
currentY = pageHeight - margin
}
// Disegna la durata
let durationText = "\(trainingExercise.duration) min"
let durationAttributedString = NSAttributedString(string: durationText, attributes: boldAttributes)
drawText(attributedString: durationAttributedString, position: CGPoint(x: margin, y: currentY), context: pdfContext)
currentY -= 20
// Disegna la descrizione con wrap text
let descriptionText = trainingExercise.exercise.exerciseDescription
let descriptionAttributedString = NSAttributedString(string: descriptionText, attributes: regularAttributes)
let maxTextWidth = pageWidth - 2 * margin
let descriptionBoundingBox = descriptionAttributedString.boundingRect(with: CGSize(width: maxTextWidth, height: .greatestFiniteMagnitude), options: [.usesLineFragmentOrigin, .usesFontLeading])
if currentY - descriptionBoundingBox.height < margin {
pdfContext.endPDFPage()
pdfContext.beginPDFPage(nil)
currentY = pageHeight - margin
}
// Disegno della descrizione con supporto per il wrap text
let descriptionFramesetter = CTFramesetterCreateWithAttributedString(descriptionAttributedString)
let descriptionPath = CGPath(rect: CGRect(x: margin, y: currentY - descriptionBoundingBox.height, width: maxTextWidth, height: descriptionBoundingBox.height), transform: nil)
let descriptionFrame = CTFramesetterCreateFrame(descriptionFramesetter, CFRangeMake(0, 0), descriptionPath, nil)
CTFrameDraw(descriptionFrame, pdfContext)
currentY -= descriptionBoundingBox.height + 10
// Disegna l'immagine sotto la descrizione
if let imagePath = trainingExercise.exercise.imagePath, let image = NSImage(contentsOfFile: imagePath) {
let imageCG = image.cgImage(forProposedRect: nil, context: nil, hints: nil)
let imageAspect = CGFloat(imageCG!.width) / CGFloat(imageCG!.height)
let targetWidth = min(maxTextWidth, CGFloat(400))
let targetHeight = targetWidth / imageAspect
if currentY - targetHeight < margin {
pdfContext.endPDFPage()
pdfContext.beginPDFPage(nil)
currentY = pageHeight - margin
}
let imageTargetRect = CGRect(x: margin, y: currentY - targetHeight, width: targetWidth, height: targetHeight)
pdfContext.draw(imageCG!, in: imageTargetRect)
currentY -= targetHeight + 10
}
// Disegna una linea sottile come separatore
pdfContext.setStrokeColor(NSColor.lightGray.cgColor)
pdfContext.setLineWidth(1.0)
let lineYPosition = currentY - 5
pdfContext.move(to: CGPoint(x: margin, y: lineYPosition))
pdfContext.addLine(to: CGPoint(x: pageWidth - margin, y: lineYPosition))
pdfContext.strokePath()
currentY -= 20 // Spazio tra esercizi
}
pdfContext.endPDFPage()
pdfContext.closePDF()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let fileName = "Allenamento_\(dateFormatter.string(from: training.date)).pdf"
if let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
let fileURL = documentsURL.appendingPathComponent(fileName)
do {
...
}
} catch {
print("Errore durante il salvataggio del PDF: \(error)")
}
}
}
private func drawText(attributedString: NSAttributedString, position: CGPoint, context: CGContext) {
let line = CTLineCreateWithAttributedString(attributedString)
context.textPosition = position
CTLineDraw(line, context)
}