crossmate

A collaborative crossword app for iOS
Log | Files | Refs | LICENSE

commit 40a77c87e7ad561df0ee8e7606c2d8b52e331158
parent a0bc1c7f6afbcb567538aa709c933442b3b70065
Author: Michael Camilleri <[email protected]>
Date:   Tue, 19 May 2026 09:39:01 +0900

Clean .puz author strings and label the credits page

The .puz to .xd converter wrote the raw Across Lite author field straight into
the Author: metadata. Those strings frequently embed the credit phrasing itself
("by Jane Doe", "Edited by John Smith"), which would then collide with any "By"
prefix at display time.

Thia commit adds displayAuthor(fromPUZAuthor:), mirroring the existing
displayTitle helper: it trims surrounding whitespace and strips a leading
case-insensitive "by "/"edited by " so the stored Author: value is a clean name.

Co-Authored-By: Claude Opus 4.7 <[email protected]>

Diffstat:
MCrossmate/Services/PUZToXDConverter.swift | 17++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/Crossmate/Services/PUZToXDConverter.swift b/Crossmate/Services/PUZToXDConverter.swift @@ -60,7 +60,7 @@ enum PUZToXDConverter { } let title = displayTitle(fromPUZTitle: stringTable.strings[0]) - let author = stringTable.strings[1] + let author = displayAuthor(fromPUZAuthor: stringTable.strings[1]) let copyright = stringTable.strings[2] let clueTexts = Array(stringTable.strings[3..<(3 + clueCount)]) let extensions = parseExtensions(in: data, from: stringTable.endOffset) @@ -264,6 +264,21 @@ enum PUZToXDConverter { return result } + /// `.puz` author strings frequently embed the credit phrasing + /// ("by Jane Doe", "Edited by John Smith"). Strip a leading + /// "by"/"edited by" and surrounding whitespace so the stored + /// `Author:` metadata is a clean name, matching the NYT path; the + /// credits view supplies the "By " label at render time. + private static func displayAuthor(fromPUZAuthor author: String) -> String { + var result = author.trimmingCharacters(in: .whitespacesAndNewlines) + for prefix in ["edited by ", "by "] where result.lowercased().hasPrefix(prefix) { + result = String(result.dropFirst(prefix.count)) + .trimmingCharacters(in: .whitespacesAndNewlines) + break + } + return result + } + private static func parseExtensions(in data: Data, from start: Int) -> [String: Data] { var extensions: [String: Data] = [:] var offset = start