Actually fix oversized PDF text — root cause was the previous fix
Empirically reproduced this outside the app (standalone WKWebView +
createPDF script, inspecting real PDF page bounds via PDFKit) instead
of guessing again. Findings:
- WKPDFConfiguration.rect left at default captures the webview's
frame size verbatim when content fits within it, and auto-grows to
a single tall page matching full scrollable content when it
overflows. It does not paginate, and setting rect explicitly just
clips to that one rect instead.
- The scrollHeight-based frame resize added last round (to "fix" this
same bug) was itself the problem: resizing the frame before calling
createPDF produced a page that didn't match the resized frame at
all (e.g. resized to 816x295, captured page came out 799x375) — a
small, badly-proportioned page that made ordinarily-sized text look
enormous relative to it. Removed entirely.
- A visible scrollbar during capture shaves ~17pt off the captured
page width. Added ::-webkit-scrollbar { display: none } to prevent
that.
Verified against a 12-message realistic conversation: clean single
page, correct proportions, readable text, matches the intended CSS
layout. The text-size-adjust/viewport-meta fix from last round turned
out to be unnecessary (computed font-size was correct all along) but
is harmless, so left in place.
This commit is contained in:
@@ -71,6 +71,7 @@ enum ConversationExportService {
|
|||||||
nonisolated private static let css = """
|
nonisolated private static let css = """
|
||||||
:root { color-scheme: light dark; }
|
:root { color-scheme: light dark; }
|
||||||
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
|
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
|
||||||
|
::-webkit-scrollbar { display: none; }
|
||||||
body { font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", sans-serif; \
|
body { font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", sans-serif; \
|
||||||
font-size: 15px; color: #1a1a1a; background: #ffffff; max-width: 820px; margin: 40px auto; \
|
font-size: 15px; color: #1a1a1a; background: #ffffff; max-width: 820px; margin: 40px auto; \
|
||||||
padding: 0 24px; line-height: 1.5; }
|
padding: 0 24px; line-height: 1.5; }
|
||||||
@@ -113,7 +114,15 @@ enum ConversationExportService {
|
|||||||
|
|
||||||
static func pdfData(name: String, messages: [Message]) async throws -> Data {
|
static func pdfData(name: String, messages: [Message]) async throws -> Data {
|
||||||
let htmlString = html(name: name, messages: messages)
|
let htmlString = html(name: name, messages: messages)
|
||||||
let webView = WKWebView(frame: NSRect(x: 0, y: 0, width: 816, height: 1056))
|
// Confirmed empirically (not guessed): with WKPDFConfiguration.rect left at its
|
||||||
|
// default, createPDF captures exactly the webview's frame size verbatim for content
|
||||||
|
// that fits within it, and auto-grows to a single tall page matching the full
|
||||||
|
// scrollable content when it overflows — it does not paginate. Manually resizing the
|
||||||
|
// frame to match content height beforehand (a previous attempt at this) actively
|
||||||
|
// breaks that: the captured page ends up NOT matching the resized frame at all,
|
||||||
|
// producing a small, oddly-proportioned page that made ordinary-sized text look
|
||||||
|
// enormous relative to it. So: set a sane starting frame and leave it alone.
|
||||||
|
let webView = WKWebView(frame: NSRect(x: 0, y: 0, width: 850, height: 1100))
|
||||||
webView.pageZoom = 1.0
|
webView.pageZoom = 1.0
|
||||||
// PDF content is baked at export time, so it can't respond to prefers-color-scheme
|
// PDF content is baked at export time, so it can't respond to prefers-color-scheme
|
||||||
// live like the HTML export does — match whatever appearance the app is in right now.
|
// live like the HTML export does — match whatever appearance the app is in right now.
|
||||||
@@ -122,14 +131,6 @@ enum ConversationExportService {
|
|||||||
webView.navigationDelegate = delegate
|
webView.navigationDelegate = delegate
|
||||||
try await delegate.load(htmlString, in: webView)
|
try await delegate.load(htmlString, in: webView)
|
||||||
|
|
||||||
// Resize to the actual rendered content height so createPDF's capture bounds match
|
|
||||||
// reality, rather than relying on its undocumented auto-sizing against the arbitrary
|
|
||||||
// initial frame — a mismatch here is a common cause of blown-up/rescaled PDF output.
|
|
||||||
if let heightValue = try? await webView.evaluateJavaScript("document.body.scrollHeight"),
|
|
||||||
let height = (heightValue as? NSNumber)?.doubleValue, height > 0 {
|
|
||||||
webView.frame = NSRect(x: 0, y: 0, width: webView.frame.width, height: height)
|
|
||||||
}
|
|
||||||
|
|
||||||
return try await withCheckedThrowingContinuation { continuation in
|
return try await withCheckedThrowingContinuation { continuation in
|
||||||
webView.createPDF(configuration: WKPDFConfiguration()) { result in
|
webView.createPDF(configuration: WKPDFConfiguration()) { result in
|
||||||
continuation.resume(with: result)
|
continuation.resume(with: result)
|
||||||
|
|||||||
Reference in New Issue
Block a user