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
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@ The release run heads these entries with the version and opens a fresh

## Unreleased

- A right-to-left document renders right-to-left. `style:writing-mode`,
`w:bidi` and `a:pPr@rtl` are read into a new `TextDirection` on
`ParagraphStyle` and `PageLayout`, and the view's root carries it as
`<html dir>`. Towards
[OpenDocument.droid#653](https://github.com/opendocument-app/OpenDocument.droid/issues/653).

- **Breaking**: `TextAlign` gains `start` and `end`, which `w:jc` now resolves
to instead of `left` / `right`; they follow the paragraph's direction.
`fo:text-align`'s same-named values stay absolute. Existing enumerators keep
their values, so only an exhaustive `switch` needs changing. Mirrored in
every binding.

- A printed sheet drops our row/column ruler and is capped to the page width
rather than cut off at the right edge. Print only; the on-screen view is
unchanged. Towards #816.
Expand Down
13 changes: 13 additions & 0 deletions apple/include/OdrCoreObjC/ODRStyle.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,22 @@ typedef NS_ENUM(NSInteger, ODRBreakType) {
ODRBreakTypeColumn,
} NS_SWIFT_NAME(BreakType);

/// `Start`/`End` name the edge `ODRTextDirection` decides.
typedef NS_ENUM(NSInteger, ODRTextAlign) {
ODRTextAlignLeft = 0,
ODRTextAlignRight,
ODRTextAlignCenter,
ODRTextAlignJustify,
ODRTextAlignStart,
ODRTextAlignEnd,
} NS_SWIFT_NAME(TextAlign);

/// The base direction a line of text runs in.
typedef NS_ENUM(NSInteger, ODRTextDirection) {
ODRTextDirectionLeftToRight = 0,
ODRTextDirectionRightToLeft,
} NS_SWIFT_NAME(TextDirection);

typedef NS_ENUM(NSInteger, ODRHorizontalAlign) {
ODRHorizontalAlignLeft = 0,
ODRHorizontalAlignCenter,
Expand Down Expand Up @@ -161,6 +170,8 @@ NS_SWIFT_NAME(ParagraphStyle)
@interface ODRParagraphStyle : NSObject
/// `ODRTextAlign`, boxed.
@property(nonatomic, readonly, nullable) NSNumber *textAlign;
/// `ODRTextDirection`, boxed; `nil` where the style says nothing.
@property(nonatomic, readonly, nullable) NSNumber *direction;
@property(nonatomic, readonly) ODRDirectionalMeasure *margin;
@property(nonatomic, readonly, nullable) ODRMeasure *lineHeight;
@property(nonatomic, readonly, nullable) ODRMeasure *textIndent;
Expand Down Expand Up @@ -250,6 +261,8 @@ NS_SWIFT_NAME(PageLayout)
@property(nonatomic, readonly) ODRDirectionalMeasure *margin;
/// `ODRColor`, boxed in an `NSValue`.
@property(nonatomic, readonly, nullable) NSValue *backgroundColor;
/// `ODRTextDirection`, boxed; `nil` where the layout says nothing.
@property(nonatomic, readonly, nullable) NSNumber *direction;

- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;
Expand Down
7 changes: 7 additions & 0 deletions apple/src/ODRStyle.mm
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@
ODR_SAME_ENUM(ODRTextAlignRight, odr::TextAlign::right);
ODR_SAME_ENUM(ODRTextAlignCenter, odr::TextAlign::center);
ODR_SAME_ENUM(ODRTextAlignJustify, odr::TextAlign::justify);
ODR_SAME_ENUM(ODRTextAlignStart, odr::TextAlign::start);
ODR_SAME_ENUM(ODRTextAlignEnd, odr::TextAlign::end);

ODR_SAME_ENUM(ODRTextDirectionLeftToRight, odr::TextDirection::left_to_right);
ODR_SAME_ENUM(ODRTextDirectionRightToLeft, odr::TextDirection::right_to_left);
ODR_SAME_ENUM(ODRHorizontalAlignLeft, odr::HorizontalAlign::left);
ODR_SAME_ENUM(ODRHorizontalAlignCenter, odr::HorizontalAlign::center);
ODR_SAME_ENUM(ODRHorizontalAlignRight, odr::HorizontalAlign::right);
Expand Down Expand Up @@ -216,6 +221,7 @@ @implementation ODRParagraphStyle
+ (instancetype)styleWithHandle:(const odr::ParagraphStyle &)handle {
ODRParagraphStyle *const result = [[ODRParagraphStyle alloc] init];
result->_textAlign = box_enum(handle.text_align);
result->_direction = box_enum(handle.direction);
result->_margin = [ODRDirectionalMeasure directionalWithHandle:handle.margin];
result->_lineHeight = box(handle.line_height);
result->_textIndent = box(handle.text_indent);
Expand Down Expand Up @@ -299,6 +305,7 @@ + (instancetype)layoutWithHandle:(const odr::PageLayout &)handle {
result->_printOrientation = box_enum(handle.print_orientation);
result->_margin = [ODRDirectionalMeasure directionalWithHandle:handle.margin];
result->_backgroundColor = box(handle.background_color);
result->_direction = box_enum(handle.direction);
return result;
}

Expand Down
6 changes: 6 additions & 0 deletions apple/swift/Style+Optionals.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ extension TextStyle {

extension ParagraphStyle {
public var alignment: TextAlign? { textAlign?.asEnum(TextAlign.self) }
public var baseDirection: TextDirection? {
direction?.asEnum(TextDirection.self)
}
}

extension TableCellStyle {
Expand All @@ -64,6 +67,9 @@ extension PageLayout {
printOrientation?.asEnum(PrintOrientation.self)
}
public var background: Color? { backgroundColor?.asColor }
public var baseDirection: TextDirection? {
direction?.asEnum(TextDirection.self)
}
}

extension Frame {
Expand Down
1 change: 1 addition & 0 deletions jni/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ add_jar(odr_java
"java/app/opendocument/core/TableStyle.java"
"java/app/opendocument/core/Text.java"
"java/app/opendocument/core/TextAlign.java"
"java/app/opendocument/core/TextDirection.java"
"java/app/opendocument/core/TextFile.java"
"java/app/opendocument/core/TextRoot.java"
"java/app/opendocument/core/TextStyle.java"
Expand Down
6 changes: 5 additions & 1 deletion jni/java/app/opendocument/core/PageLayout.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,21 @@ public final class PageLayout {
public final PrintOrientation printOrientation;
public final DirectionalMeasure margin;
public final Color backgroundColor;
/** The base direction the page's text runs in; {@code null} if the layout says nothing. */
public final TextDirection direction;

PageLayout(
Measure width,
Measure height,
int printOrientation,
DirectionalMeasure margin,
Color backgroundColor) {
Color backgroundColor,
int direction) {
this.width = width;
this.height = height;
this.printOrientation = PrintOrientation.fromNative(printOrientation);
this.margin = margin;
this.backgroundColor = backgroundColor;
this.direction = TextDirection.fromNative(direction);
}
}
4 changes: 4 additions & 0 deletions jni/java/app/opendocument/core/ParagraphStyle.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
/** Style of a paragraph. Mirrors {@code odr::ParagraphStyle}; fields may be {@code null}. */
public final class ParagraphStyle {
public final TextAlign textAlign;
/** The base direction the paragraph's text runs in; {@code null} if the style says nothing. */
public final TextDirection direction;
public final DirectionalMeasure margin;
public final Measure lineHeight;
public final Measure textIndent;
Expand All @@ -13,12 +15,14 @@ public final class ParagraphStyle {

ParagraphStyle(
int textAlign,
int direction,
DirectionalMeasure margin,
Measure lineHeight,
Measure textIndent,
int breakBefore,
int breakAfter) {
this.textAlign = TextAlign.fromNative(textAlign);
this.direction = TextDirection.fromNative(direction);
this.margin = margin;
this.lineHeight = lineHeight;
this.textIndent = textIndent;
Expand Down
2 changes: 1 addition & 1 deletion jni/java/app/opendocument/core/TextAlign.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

/** Mirrors {@code odr::TextAlign}; constant order must match the C++ declaration. */
public enum TextAlign {
LEFT, RIGHT, CENTER, JUSTIFY;
LEFT, RIGHT, CENTER, JUSTIFY, START, END;

static TextAlign fromNative(int code) {
return code < 0 ? null : values()[code];
Expand Down
14 changes: 14 additions & 0 deletions jni/java/app/opendocument/core/TextDirection.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package app.opendocument.core;

/** Mirrors {@code odr::TextDirection}; constant order must match the C++ declaration. */
public enum TextDirection {
LEFT_TO_RIGHT, RIGHT_TO_LEFT;

static TextDirection fromNative(int code) {
return code < 0 ? null : values()[code];
}

int toNative() {
return ordinal();
}
}
9 changes: 5 additions & 4 deletions jni/src/jni_style.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -272,9 +272,10 @@ jobject make_text_style(JNIEnv *env, const odr::TextStyle &style) {
jobject make_paragraph_style(JNIEnv *env, const odr::ParagraphStyle &style) {
return new_object(
env, "app/opendocument/core/ParagraphStyle",
"(ILapp/opendocument/core/DirectionalMeasure;"
"(IILapp/opendocument/core/DirectionalMeasure;"
"Lapp/opendocument/core/Measure;Lapp/opendocument/core/Measure;II)V",
enum_code(style.text_align), make_directional_measure(env, style.margin),
enum_code(style.text_align), enum_code(style.direction),
make_directional_measure(env, style.margin),
make_measure(env, style.line_height),
make_measure(env, style.text_indent), enum_code(style.break_before),
enum_code(style.break_after));
Expand Down Expand Up @@ -333,11 +334,11 @@ jobject make_page_layout(JNIEnv *env, const odr::PageLayout &layout) {
env, "app/opendocument/core/PageLayout",
"(Lapp/opendocument/core/Measure;Lapp/opendocument/core/Measure;I"
"Lapp/opendocument/core/DirectionalMeasure;"
"Lapp/opendocument/core/Color;)V",
"Lapp/opendocument/core/Color;I)V",
make_measure(env, layout.width), make_measure(env, layout.height),
enum_code(layout.print_orientation),
make_directional_measure(env, layout.margin),
make_color(env, layout.background_color));
make_color(env, layout.background_color), enum_code(layout.direction));
}

jobject make_table_dimensions(JNIEnv *env,
Expand Down
12 changes: 10 additions & 2 deletions python/src/bind_style.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,13 @@ void odr_python::bind_style(py::module_ &m) {
.value("left", odr::TextAlign::left)
.value("right", odr::TextAlign::right)
.value("center", odr::TextAlign::center)
.value("justify", odr::TextAlign::justify);
.value("justify", odr::TextAlign::justify)
.value("start", odr::TextAlign::start)
.value("end", odr::TextAlign::end);

py::enum_<odr::TextDirection>(m, "TextDirection")
.value("left_to_right", odr::TextDirection::left_to_right)
.value("right_to_left", odr::TextDirection::right_to_left);

py::enum_<odr::HorizontalAlign>(m, "HorizontalAlign")
.value("left", odr::HorizontalAlign::left)
Expand Down Expand Up @@ -149,6 +155,7 @@ void odr_python::bind_style(py::module_ &m) {
py::class_<odr::ParagraphStyle>(m, "ParagraphStyle")
.def(py::init<>())
.def_readwrite("text_align", &odr::ParagraphStyle::text_align)
.def_readwrite("direction", &odr::ParagraphStyle::direction)
.def_readwrite("margin", &odr::ParagraphStyle::margin)
.def_readwrite("line_height", &odr::ParagraphStyle::line_height)
.def_readwrite("text_indent", &odr::ParagraphStyle::text_indent)
Expand Down Expand Up @@ -197,5 +204,6 @@ void odr_python::bind_style(py::module_ &m) {
.def_readwrite("height", &odr::PageLayout::height)
.def_readwrite("print_orientation", &odr::PageLayout::print_orientation)
.def_readwrite("margin", &odr::PageLayout::margin)
.def_readwrite("background_color", &odr::PageLayout::background_color);
.def_readwrite("background_color", &odr::PageLayout::background_color)
.def_readwrite("direction", &odr::PageLayout::direction);
}
6 changes: 6 additions & 0 deletions src/odr/internal/html/common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <odr/html.hpp>
#include <odr/internal/abstract/html_service.hpp>
#include <odr/quantity.hpp>
#include <odr/style.hpp>

namespace odr {
struct Color;
Expand All @@ -34,11 +35,16 @@ struct WritingState {
[[nodiscard]] HtmlResources &resources() const { return *m_resources; }
[[nodiscard]] const Logger &logger() const { return *m_logger; }

/// The view's base direction, stated on its root.
[[nodiscard]] TextDirection direction() const { return m_direction; }
void set_direction(const TextDirection direction) { m_direction = direction; }

private:
HtmlWriter *m_out;
const HtmlConfig *m_config;
HtmlResources *m_resources;
const Logger *m_logger;
TextDirection m_direction{TextDirection::left_to_right};
};

/// Writes the viewport meta tag. Precedence: `config.viewport_content` (raw,
Expand Down
39 changes: 37 additions & 2 deletions src/odr/internal/html/document.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,38 @@ std::optional<double> document_content_pixels(const Document &document,
return result;
}

/// A spreadsheet states its direction per table, which is not read yet.
TextDirection document_direction(const Document &document) {
const Element root = document.root_element();

std::optional<TextDirection> result;
switch (document.document_type()) {
case DocumentType::text:
result = root.as_text_root().page_layout().direction;
break;
case DocumentType::presentation:
for (const Element child : root.children()) {
result = child.as_slide().page_layout().direction;
if (result.has_value()) {
break;
}
}
break;
case DocumentType::drawing:
for (const Element child : root.children()) {
result = child.as_page().page_layout().direction;
if (result.has_value()) {
break;
}
}
break;
default:
break;
}

return result.value_or(TextDirection::left_to_right);
}

/// A spreadsheet answers the viewport question with its own mode.
std::optional<HtmlViewportMode>
viewport_mode_override(const Document &document, const HtmlConfig &config) {
Expand All @@ -121,14 +153,17 @@ viewport_mode_override(const Document &document, const HtmlConfig &config) {

/// @p name titles the view; empty when the whole document is written as one
/// file, which no one view names.
void front(const Document &document, const WritingState &state,
void front(const Document &document, WritingState &state,
const std::string &name,
const std::optional<double> content_pixels) {
HtmlWriter &out = state.out();

const bool paged_content = is_paged_content(document, state.config());

out.write_begin();
state.set_direction(document_direction(document));

out.write_begin(HtmlElementOptions().set_attributes(HtmlAttributesVector{
{"dir", translate_text_direction(state.direction())}}));
out.write_header_begin();
out.write_header_charset("UTF-8");
out.write_header_title(
Expand Down
3 changes: 2 additions & 1 deletion src/odr/internal/html/document_element.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,8 @@ void html::translate_paragraph(const Element &element,
state.out().write_element_begin(
"x-p",
HtmlElementOptions().set_inline(true).set_style(
"display:block;" + translate_paragraph_style(paragraph.style()) +
"display:block;" +
translate_paragraph_style(paragraph.style(), state.direction()) +
translate_block_font_style(paragraph.text_style())));
if (!marker.empty()) {
state.out().write_element_begin(
Expand Down
24 changes: 23 additions & 1 deletion src/odr/internal/html/document_style.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,21 @@ const char *html::translate_text_align(const TextAlign text_align) {
return "center";
case TextAlign::justify:
return "justify";
case TextAlign::start:
return "start";
case TextAlign::end:
return "end";
default:
return ""; // TODO log
}
}

const char *html::translate_text_direction(const TextDirection direction) {
switch (direction) {
case TextDirection::left_to_right:
return "ltr";
case TextDirection::right_to_left:
return "rtl";
default:
return ""; // TODO log
}
Expand Down Expand Up @@ -232,8 +247,15 @@ std::string html::translate_block_font_style(const TextStyle &text_style) {
}

std::string
html::translate_paragraph_style(const ParagraphStyle &paragraph_style) {
html::translate_paragraph_style(const ParagraphStyle &paragraph_style,
const TextDirection base) {
std::string result;
if (const std::optional<TextDirection> direction = paragraph_style.direction;
direction.has_value() && *direction != base) {
result.append("direction:")
.append(translate_text_direction(*direction))
.append(";");
}
if (const std::optional<TextAlign> text_align = paragraph_style.text_align;
text_align.has_value()) {
result.append("text-align:")
Expand Down
Loading
Loading