Explorar el Código

Export filled CV PDF from the on-screen composite so styling matches the preview.

dataWithPDF often strips layer-backed chrome; rasterising via cacheDisplay preserves it, with a vector fallback.

Co-authored-by: Cursor <cursoragent@cursor.com>
AhtashamShahzad1 hace 3 semanas
padre
commit
d57207a2d3
Se han modificado 1 ficheros con 38 adiciones y 2 borrados
  1. 38 2
      App for Indeed/Views/CVFilledPreviewPageView.swift

+ 38 - 2
App for Indeed/Views/CVFilledPreviewPageView.swift

@@ -63,7 +63,7 @@ final class CVFilledPreviewPageView: NSView {
63 63
         editCheckbox.target = self
64 64
         editCheckbox.action = #selector(didToggleEditMode)
65 65
 
66
-        let subtitle = NSTextField(wrappingLabelWithString: "Layout matches the CV Maker thumbnail for this template. Turn on editing to adjust wording, then export a vector PDF.")
66
+        let subtitle = NSTextField(wrappingLabelWithString: "Layout matches the CV Maker thumbnail for this template. Turn on editing to adjust wording, then export a PDF that matches what you see here (fonts, columns, colours, and rules).")
67 67
         subtitle.font = .systemFont(ofSize: 12, weight: .regular)
68 68
         subtitle.textColor = Self.secondaryText
69 69
         subtitle.maximumNumberOfLines = 0
@@ -165,7 +165,10 @@ final class CVFilledPreviewPageView: NSView {
165 165
         doc.layoutSubtreeIfNeeded()
166 166
         let bounds = doc.bounds
167 167
         guard !bounds.isEmpty else { return }
168
-        let data = doc.dataWithPDF(inside: bounds)
168
+        // `dataWithPDF` uses the print pipeline and often drops layer-backed chrome (card fill,
169
+        // borders, hairlines, tinted sidebars) while keeping text — so the PDF looks like plain
170
+        // text. Rasterising what is actually drawn on screen preserves the full layout.
171
+        let data = doc.pdfDataMatchingScreenAppearance() ?? doc.dataWithPDF(inside: bounds)
169 172
         guard !data.isEmpty else {
170 173
             presentExportError("The résumé could not be rendered to PDF (empty output). Try scrolling the preview so it lays out, then export again.")
171 174
             return
@@ -215,3 +218,36 @@ final class CVFilledPreviewPageView: NSView {
215 218
         }
216 219
     }
217 220
 }
221
+
222
+// MARK: - PDF export (screen-accurate)
223
+
224
+private extension NSView {
225
+
226
+    /// Single-page PDF that matches the composited on-screen appearance, including
227
+    /// `CALayer` backgrounds, borders, and rules. Callers fall back to `dataWithPDF` when this returns nil.
228
+    func pdfDataMatchingScreenAppearance() -> Data? {
229
+        layoutSubtreeIfNeeded()
230
+        let rect = bounds
231
+        guard rect.width > 1, rect.height > 1 else { return nil }
232
+
233
+        guard let rep = bitmapImageRepForCachingDisplay(in: rect) else { return nil }
234
+        cacheDisplay(in: rect, to: rep)
235
+        guard let cgImage = rep.cgImage else { return nil }
236
+
237
+        let data = NSMutableData()
238
+        guard let consumer = CGDataConsumer(data: data as CFMutableData) else { return nil }
239
+        var mediaBox = CGRect(origin: .zero, size: NSSize(width: rect.width, height: rect.height))
240
+        guard let ctx = CGContext(consumer: consumer, mediaBox: &mediaBox, nil) else { return nil }
241
+
242
+        ctx.beginPDFPage(nil)
243
+        ctx.interpolationQuality = .high
244
+        // Draw without an extra Y-flip: `cacheDisplay`’s bitmap + `cgImage` already match PDF user space
245
+        // on macOS here; a translate/scale(-1) was inverting the whole page in Preview.
246
+        ctx.draw(cgImage, in: mediaBox)
247
+        ctx.endPDFPage()
248
+        ctx.closePDF()
249
+
250
+        let out = data as Data
251
+        return out.isEmpty ? nil : out
252
+    }
253
+}