diff --git a/CHANGELOG.md b/CHANGELOG.md index b7a887c0e..ea122f9ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,9 @@ The release run heads these entries with the version and opens a fresh ## Unreleased +- Apple `Measure(string:)` is now failable. An invalid length returns `nil` + instead of letting a C++ exception escape into Swift. + - Python, Java and Apple file/image/font reads report stream failures instead of returning partial data after a ZIP checksum or other read error. diff --git a/apple/include/OdrCoreObjC/ODRStyle.h b/apple/include/OdrCoreObjC/ODRStyle.h index 2223bd222..729357170 100644 --- a/apple/include/OdrCoreObjC/ODRStyle.h +++ b/apple/include/OdrCoreObjC/ODRStyle.h @@ -96,9 +96,9 @@ NS_SWIFT_NAME(Measure) /// Magnitude and unit as odrcore writes them, e.g. `12pt`. @property(nonatomic, readonly, copy) NSString *stringValue; -/// A css length as odrcore writes one, e.g. `3mm`. A magnitude with no unit -/// reads as unitless. -- (instancetype)initWithString:(NSString *)string; +/// Parses a length such as `3mm` or a unitless magnitude; returns nil on +/// failure. +- (nullable instancetype)initWithString:(NSString *)string; - (instancetype)initWithMagnitude:(double)magnitude unit:(NSString *)unit; - (instancetype)init NS_UNAVAILABLE; diff --git a/apple/src/ODRStyle.mm b/apple/src/ODRStyle.mm index f39b489e2..c5f13c231 100644 --- a/apple/src/ODRStyle.mm +++ b/apple/src/ODRStyle.mm @@ -121,12 +121,16 @@ - (NSString *)stringValue { return guarded_value([&] { return to_nsstring(_handle->to_string()); }, @""); } -- (instancetype)initWithString:(NSString *)string { +- (nullable instancetype)initWithString:(NSString *)string { if ((self = [super init]) == nil) { return nil; } - _handle = odr::Measure(to_string(string)); - return self; + return guarded_value( + [&]() -> ODRMeasure * { + _handle = odr::Measure(to_string(string)); + return self; + }, + nil); } - (instancetype)initWithMagnitude:(double)magnitude unit:(NSString *)unit { diff --git a/apple/tests/OdrCoreTests.swift b/apple/tests/OdrCoreTests.swift index 0e97653cf..b844f78ac 100644 --- a/apple/tests/OdrCoreTests.swift +++ b/apple/tests/OdrCoreTests.swift @@ -778,6 +778,19 @@ final class PdfAnnotationTests: XCTestCase { } } +final class MeasureTests: XCTestCase { + func testConstructionReturnsNilOnFailure() throws { + let parsed = try XCTUnwrap(Measure(string: "2.5cm")) + XCTAssertEqual(parsed.magnitude, 2.5) + XCTAssertEqual(parsed.unit, "cm") + XCTAssertEqual(Measure(magnitude: 2.5, unit: "cm").stringValue, "2.5cm") + XCTAssertEqual(Measure(string: "12")?.unit, "") + for invalid in ["", "invalid", "1e999px"] { + XCTAssertNil(Measure(string: invalid), invalid) + } + } +} + final class TableAddressTests: XCTestCase { func testRoundTrips() throws { XCTAssertEqual(TableAddress.columnNumber(from: "C"), 2)