Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ The release run heads these entries with the version and opens a fresh
chords, and `odr.onSelectionChange` for a host's buttons. A toggle on a
collapsed caret marks the next typed text. Scope `paragraph` refuses it.

- `Text::set_style` in every binding: `Text.set_style` in python,
`Text.setStyle` in Java, `-[ODRText setStyle:error:]` in Objective-C, both
with a constructible `TextStyle`, and `setTextStyle(id, style)` in npm.

- A run that is both underlined and struck through renders both lines. The
page wrote two `text-decoration` declarations, and the second replaced the
first.
Expand Down
4 changes: 4 additions & 0 deletions apple/include/OdrCoreObjC/ODRDocumentElement.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,10 @@ NS_SWIFT_NAME(Text)
/// Replaces the text. Only meaningful on an editable document.
- (BOOL)setContent:(NSString *)content error:(NSError **)error;
@property(nonatomic, readonly) ODRTextStyle *style;
/// States the non-nil properties of `style` on the run and leaves the rest;
/// a `backgroundColor` with alpha 0 removes a highlight. `fontName`,
/// `fontShadow` and `fontPosition` refuse with `ODRErrorUnsupportedOperation`.
- (BOOL)setStyle:(ODRTextStyle *)style error:(NSError **)error;
@end

/// `odr::Link`.
Expand Down
30 changes: 17 additions & 13 deletions apple/include/OdrCoreObjC/ODRStyle.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,28 +141,32 @@ NS_SWIFT_NAME(DirectionalString)
/// Every property is optional, and `nil` means the document did not specify it
/// rather than that it is off. The enum-valued ones are boxed in `NSNumber`
/// for that reason; unbox with `ODRFontWeight(rawValue:)`.
///
/// A caller builds one for `-[ODRText setStyle:error:]`: `init` leaves every
/// property `nil`, and a `nil` property is left alone on the run.
NS_SWIFT_NAME(TextStyle)
@interface ODRTextStyle : NSObject
@property(nonatomic, readonly, nullable, copy) NSString *fontName;
@property(nonatomic, readonly, nullable) ODRMeasure *fontSize;
/// Read only: it borrows from the document, and `setStyle:` refuses it.
@property(nonatomic, nullable, copy) NSString *fontName;
@property(nonatomic, nullable) ODRMeasure *fontSize;
/// `ODRFontWeight`, boxed.
@property(nonatomic, readonly, nullable) NSNumber *fontWeight;
@property(nonatomic, nullable) NSNumber *fontWeight;
/// `ODRFontStyle`, boxed.
@property(nonatomic, readonly, nullable) NSNumber *fontStyle;
@property(nonatomic, nullable) NSNumber *fontStyle;
/// `BOOL`, boxed.
@property(nonatomic, readonly, nullable) NSNumber *fontUnderline;
@property(nonatomic, nullable) NSNumber *fontUnderline;
/// `BOOL`, boxed.
@property(nonatomic, readonly, nullable) NSNumber *fontLineThrough;
@property(nonatomic, readonly, nullable, copy) NSString *fontShadow;
/// `ODRColor`, boxed in an `NSValue`.
@property(nonatomic, readonly, nullable) NSValue *fontColor;
@property(nonatomic, nullable) NSNumber *fontLineThrough;
@property(nonatomic, nullable, copy) NSString *fontShadow;
/// `ODRColor`, boxed in an `NSValue`.
@property(nonatomic, readonly, nullable) NSValue *backgroundColor;
@property(nonatomic, nullable) NSValue *fontColor;
/// `ODRColor`, boxed in an `NSValue`; an alpha of 0 takes a highlight away.
@property(nonatomic, nullable) NSValue *backgroundColor;
/// `ODRFontPosition`, boxed.
@property(nonatomic, readonly, nullable) NSNumber *fontPosition;
@property(nonatomic, nullable) NSNumber *fontPosition;

- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;
/// Every property `nil`.
- (instancetype)init;
@end

/// Paragraph style — `odr::ParagraphStyle`.
Expand Down
7 changes: 7 additions & 0 deletions apple/src/ODRDocumentElement.mm
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,13 @@ - (BOOL)setContent:(NSString *)content error:(NSError **)error {
});
}

- (BOOL)setStyle:(ODRTextStyle *)style error:(NSError **)error {
return guarded(error, [&] {
self.handle.as_text().set_style([style handle]);
return YES;
});
}

- (ODRTextStyle *)style {
return guarded_value(
[&]() -> ODRTextStyle * {
Expand Down
3 changes: 3 additions & 0 deletions apple/src/ODRPrivate.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ NS_ASSUME_NONNULL_BEGIN

@interface ODRTextStyle (Private)
+ (instancetype)styleWithHandle:(const odr::TextStyle &)handle;
/// The set properties as a `TextStyle`; throws `UnsupportedOperation` for a
/// `fontName`, which the C++ one borrows from a document.
- (odr::TextStyle)handle;
@end

@interface ODRParagraphStyle (Private)
Expand Down
43 changes: 43 additions & 0 deletions apple/src/ODRStyle.mm
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#import "ODRInternal.h"
#import "ODRPrivate.h"

#include <odr/exceptions.hpp>
#include <odr/style.hpp>

#include <optional>
Expand Down Expand Up @@ -89,6 +90,12 @@
return value.has_value() ? to_nsstring(*value) : nil;
}

odr::Color unbox_color(NSValue *const value) {
ODRColor color{};
[value getValue:&color size:sizeof(color)];
return {color.red, color.green, color.blue, color.alpha};
}

} // namespace

#pragma mark - ODRMeasure
Expand Down Expand Up @@ -214,6 +221,42 @@ + (instancetype)styleWithHandle:(const odr::TextStyle &)handle {
return result;
}

- (odr::TextStyle)handle {
odr::TextStyle result;
if (_fontName != nil) {
throw odr::UnsupportedOperation();
}
if (_fontSize != nil) {
result.font_size = _fontSize.handle;
}
if (_fontWeight != nil) {
result.font_weight = static_cast<odr::FontWeight>(_fontWeight.integerValue);
}
if (_fontStyle != nil) {
result.font_style = static_cast<odr::FontStyle>(_fontStyle.integerValue);
}
if (_fontUnderline != nil) {
result.font_underline = _fontUnderline.boolValue != NO;
}
if (_fontLineThrough != nil) {
result.font_line_through = _fontLineThrough.boolValue != NO;
}
if (_fontShadow != nil) {
result.font_shadow = to_string(_fontShadow);
}
if (_fontColor != nil) {
result.font_color = unbox_color(_fontColor);
}
if (_backgroundColor != nil) {
result.background_color = unbox_color(_backgroundColor);
}
if (_fontPosition != nil) {
result.font_position =
static_cast<odr::FontPosition>(_fontPosition.integerValue);
}
return result;
}

@end

@implementation ODRParagraphStyle
Expand Down
38 changes: 38 additions & 0 deletions apple/tests/OdrCoreTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,44 @@ final class DocumentSaveTests: XCTestCase {
.asDocumentFile().document()
}

func testSetStyleMarksARun() throws {
let document = try self.document()
let root = try XCTUnwrap(try document.rootElement())
let text = try XCTUnwrap(root.firstDescendant(ofType: Text.self))

let style = TextStyle()
style.fontWeight = NSNumber(value: FontWeight.bold.rawValue)
style.fontUnderline = true
style.fontSize = Measure(string: "14pt")
try text.setStyle(style)

let saved = try XCTUnwrap(try document.saveToMemory())
let path = URL(fileURLWithPath: try temporaryDirectory())
.appendingPathComponent("styled.odt")
try saved.write(to: path)

let reloaded = try DecodedFile.decode(path: path.path)
.asDocumentFile().document()
let reloadedRoot = try XCTUnwrap(try reloaded.rootElement())
let styled = try XCTUnwrap(reloadedRoot.firstDescendant(ofType: Text.self)).style
XCTAssertEqual(styled.fontWeight?.intValue, FontWeight.bold.rawValue)
XCTAssertEqual(styled.fontUnderline?.boolValue, true)
XCTAssertEqual(styled.fontSize?.stringValue, "14pt")
XCTAssertNil(styled.fontStyle)
}

func testSetStyleRefusesAFontName() throws {
let document = try self.document()
let root = try XCTUnwrap(try document.rootElement())
let text = try XCTUnwrap(root.firstDescendant(ofType: Text.self))

let style = TextStyle()
style.fontName = "Comic Sans"
XCTAssertThrowsError(try text.setStyle(style)) { error in
XCTAssertEqual((error as NSError).code, ODRError.unsupportedOperation.rawValue)
}
}

func testSaveToMemoryCarriesAnEdit() throws {
let document = try self.document()
XCTAssertTrue(document.isSavable)
Expand Down
12 changes: 7 additions & 5 deletions docs/design/document-editing.md
Original file line number Diff line number Diff line change
Expand Up @@ -362,11 +362,10 @@ that names the frame rather than a range across text.

## Inline formatting

Status: **landed**, but for the bindings; the order of work below says what
is in. It covers what a reader changes on a stretch of text without
changing the text: bold, italic, underline, strikethrough, highlight, colour
and size. Font name, superscript and subscript are not in it; nothing asked
for them, and each is the same shape once these seven are in.
Status: **landed.** It covers what a reader changes on a stretch of text
without changing the text: bold, italic, underline, strikethrough, highlight,
colour and size. Font name, superscript and subscript are not in it; nothing
asked for them, and each is the same shape once these seven are in.

### What the reader changes, and where each format keeps it

Expand Down Expand Up @@ -580,6 +579,9 @@ Each step is a pull request that builds and tests on its own.
4. **The browser.** `format()`, `onSelectionChange`, the four input types,
the word rule for a collapsed caret, and a check page in
`test/browser/text` asserting the log of each gesture. **Landed.**
5. **The bindings.** `Text::set_style` in python, Java, Objective-C and the
npm package, the last taking the page's style object and replaying it
through the envelope. **Landed.**

### Open questions

Expand Down
11 changes: 11 additions & 0 deletions jni/java/app/opendocument/core/Text.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ public void setContent(String text) {
setContentNative(handle(), text);
}

/**
* States the non-null fields of {@code style} on the run and leaves the rest; a {@code
* backgroundColor} with alpha 0 removes a highlight. {@code fontName}, {@code fontShadow} and
* {@code fontPosition} are refused.
*/
public void setStyle(TextStyle style) {
setStyleNative(handle(), style);
}

public TextStyle style() {
return styleNative(handle());
}
Expand All @@ -22,5 +31,7 @@ public TextStyle style() {

private native void setContentNative(long handle, String text);

private native void setStyleNative(long handle, TextStyle style);

private native TextStyle styleNative(long handle);
}
31 changes: 20 additions & 11 deletions jni/java/app/opendocument/core/TextStyle.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
package app.opendocument.core;

/** Style of a text run. Mirrors {@code odr::TextStyle}; fields may be {@code null}. */
/**
* Style of a text run. Mirrors {@code odr::TextStyle}; a {@code null} field is one the document
* does not state. A caller builds one for {@link Text#setStyle}: every field left {@code null} is
* left alone on the run.
*/
public final class TextStyle {
public final String fontName;
public final Measure fontSize;
public final FontWeight fontWeight;
public final FontStyle fontStyle;
public final Boolean fontUnderline;
public final Boolean fontLineThrough;
public final String fontShadow;
public final Color fontColor;
public final Color backgroundColor;
public final FontPosition fontPosition;
/** Read only: it borrows from the document, and {@link Text#setStyle} refuses it. */
public String fontName;
public Measure fontSize;
public FontWeight fontWeight;
public FontStyle fontStyle;
public Boolean fontUnderline;
public Boolean fontLineThrough;
public String fontShadow;
public Color fontColor;
/** An alpha of 0 takes a highlight away. */
public Color backgroundColor;
public FontPosition fontPosition;

/** Every field {@code null}. */
public TextStyle() {}

TextStyle(
String fontName,
Expand Down
3 changes: 3 additions & 0 deletions jni/src/jni_convert.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ jobject make_file_type_capabilities(JNIEnv *env,

jobject html_config_to_java(JNIEnv *env, const odr::HtmlConfig &config);
odr::HtmlConfig html_config_from_java(JNIEnv *env, jobject config);
/// The fields a Java `TextStyle` states; a `fontName` is refused, since the
/// C++ one borrows from a document.
odr::TextStyle text_style_from_java(JNIEnv *env, jobject style);

/// Optional enum to a Java-side code; -1 encodes absent.
template <typename E> jint enum_code(const std::optional<E> &value) {
Expand Down
9 changes: 9 additions & 0 deletions jni/src/jni_document.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,15 @@ Java_app_opendocument_core_Text_setContentNative(JNIEnv *env, jobject,
[&] { element(handle).as_text().set_content(to_string(env, text)); });
}

extern "C" JNIEXPORT void JNICALL
Java_app_opendocument_core_Text_setStyleNative(JNIEnv *env, jobject,
jlong handle, jobject style) {
guarded(env, [&] {
element(handle).as_text().set_style(
odr_jni::text_style_from_java(env, style));
});
}

extern "C" JNIEXPORT jobject JNICALL
Java_app_opendocument_core_Text_styleNative(JNIEnv *env, jobject,
jlong handle) {
Expand Down
Loading
Loading