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
1 change: 1 addition & 0 deletions jni/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ add_jar(odr_java
"java/app/opendocument/core/HtmlService.java"
"java/app/opendocument/core/HtmlTableGridlines.java"
"java/app/opendocument/core/HtmlView.java"
"java/app/opendocument/core/HtmlViewportMode.java"
"java/app/opendocument/core/HttpServer.java"
"java/app/opendocument/core/Image.java"
"java/app/opendocument/core/ImageFile.java"
Expand Down
7 changes: 7 additions & 0 deletions jni/java/app/opendocument/core/HtmlConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ public final class HtmlConfig {
public boolean spreadsheetLimitByContent = true;
public HtmlTableGridlines spreadsheetGridlines = HtmlTableGridlines.SOFT;

/** Initial zoom on mobile. */
public HtmlViewportMode viewportMode = HtmlViewportMode.AUTOMATIC;
/** Overrides {@link #viewportMode} for spreadsheet content; {@code null} keeps it. */
public HtmlViewportMode spreadsheetViewportMode;
/** Raw {@code content} for the viewport meta tag; overrides the modes above when set. */
public String viewportContent;

public boolean formatHtml = false;
public int htmlIndent = 1;
public String htmlIndentString = "\t";
Expand Down
14 changes: 14 additions & 0 deletions jni/java/app/opendocument/core/HtmlViewportMode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package app.opendocument.core;

/** Mirrors {@code odr::HtmlViewportMode}; constant order must match the C++ declaration. */
public enum HtmlViewportMode {
AUTOMATIC, FIT_WIDTH, ACTUAL_SIZE, NONE;

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

int toNative() {
return ordinal();
}
}
28 changes: 28 additions & 0 deletions jni/src/jni_style.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,17 @@ jobject html_config_to_java(JNIEnv *env, const odr::HtmlConfig &config) {
"Lapp/opendocument/core/HtmlTableGridlines;",
enum_from_code(env, "app/opendocument/core/HtmlTableGridlines",
static_cast<jint>(config.spreadsheet_gridlines)));
set_object("viewportMode", "Lapp/opendocument/core/HtmlViewportMode;",
enum_from_code(env, "app/opendocument/core/HtmlViewportMode",
static_cast<jint>(config.viewport_mode)));
set_object(
"spreadsheetViewportMode", "Lapp/opendocument/core/HtmlViewportMode;",
enum_from_code(env, "app/opendocument/core/HtmlViewportMode",
config.spreadsheet_viewport_mode.has_value()
? static_cast<jint>(*config.spreadsheet_viewport_mode)
: -1));
set_object("viewportContent", "Ljava/lang/String;",
make_string_opt(env, config.viewport_content));
set_boolean("formatHtml", config.format_html);
set_int("htmlIndent", config.html_indent);
set_string("htmlIndentString", config.html_indent_string);
Expand Down Expand Up @@ -464,6 +475,23 @@ odr::HtmlConfig html_config_from_java(JNIEnv *env, jobject config) {
result.spreadsheet_gridlines = static_cast<odr::HtmlTableGridlines>(code);
}
}
{
const jint code = enum_ordinal(
env,
get_object("viewportMode", "Lapp/opendocument/core/HtmlViewportMode;"));
if (code >= 0) {
result.viewport_mode = static_cast<odr::HtmlViewportMode>(code);
}
}
{
const jint code = enum_ordinal(
env, get_object("spreadsheetViewportMode",
"Lapp/opendocument/core/HtmlViewportMode;"));
result.spreadsheet_viewport_mode =
code < 0 ? std::nullopt
: std::make_optional(static_cast<odr::HtmlViewportMode>(code));
}
result.viewport_content = get_string_opt("viewportContent");
result.format_html = get_boolean("formatHtml");
result.html_indent = static_cast<std::uint8_t>(get_int("htmlIndent"));
result.html_indent_string = get_string("htmlIndentString");
Expand Down
53 changes: 53 additions & 0 deletions jni/tests/app/opendocument/core/HtmlTest.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package app.opendocument.core;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assumptions.assumeTrue;

Expand All @@ -22,12 +23,64 @@ private Html translateOffline(Path input) throws IOException {
return service.bringOffline(output.toString());
}

private String renderOdt(HtmlConfig config) throws IOException {
Path cache = Files.createTempDirectory(tempDir, "render");
DecodedFile file = Odr.open(TestFiles.odtFile(tempDir).toString());
HtmlService service = Html.translate(file, cache.toString(), config);
return service.listViews().get(0).writeHtml().html;
}

@Test
void htmlConfigDefaults() {
HtmlConfig config = new HtmlConfig();
assertTrue(config.embedImages);
assertTrue(!config.editable);
assertEquals(HtmlTableGridlines.SOFT, config.spreadsheetGridlines);
assertEquals(HtmlViewportMode.AUTOMATIC, config.viewportMode);
assertNull(config.spreadsheetViewportMode);
assertNull(config.viewportContent);
}

@Test
void viewportConfigRoundTrips() throws IOException {
assumeTrue(TestFiles.hasCoreData(), "odr core data path not available");
HtmlConfig config = new HtmlConfig();
config.viewportMode = HtmlViewportMode.FIT_WIDTH;
config.spreadsheetViewportMode = HtmlViewportMode.ACTUAL_SIZE;
config.viewportContent = "width=420";

Path cache = Files.createDirectories(tempDir.resolve("cache"));
DecodedFile file = Odr.open(TestFiles.odtFile(tempDir).toString());
HtmlConfig readBack = Html.translate(file, cache.toString(), config).config();

assertEquals(HtmlViewportMode.FIT_WIDTH, readBack.viewportMode);
assertEquals(HtmlViewportMode.ACTUAL_SIZE, readBack.spreadsheetViewportMode);
assertEquals("width=420", readBack.viewportContent);
}

/** The C++ suite covers the mode matrix; this only proves the config crosses JNI. */
@Test
void viewportModeReachesTheHtml() throws IOException {
assumeTrue(TestFiles.hasCoreData(), "odr core data path not available");

// A text document without margins is reflowing content, so `AUTOMATIC`
// resolves to `ACTUAL_SIZE`.
String automatic = renderOdt(new HtmlConfig());
assertTrue(
automatic.contains(
"<meta name=\"viewport\""
+ " content=\"width=device-width,initial-scale=1.0,user-scalable=yes\"/>"));

HtmlConfig fitWidth = new HtmlConfig();
fitWidth.viewportMode = HtmlViewportMode.FIT_WIDTH;
assertTrue(
renderOdt(fitWidth)
.contains(
"<meta name=\"viewport\" content=\"width=device-width,user-scalable=yes\"/>"));

HtmlConfig raw = new HtmlConfig();
raw.viewportContent = "width=420";
assertTrue(renderOdt(raw).contains("<meta name=\"viewport\" content=\"width=420\"/>"));
}

@Test
Expand Down
10 changes: 10 additions & 0 deletions python/src/bind_html.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ void odr_python::bind_html(py::module_ &m) {
.value("soft", odr::HtmlTableGridlines::soft)
.value("hard", odr::HtmlTableGridlines::hard);

py::enum_<odr::HtmlViewportMode>(m, "HtmlViewportMode")
.value("automatic", odr::HtmlViewportMode::automatic)
.value("fit_width", odr::HtmlViewportMode::fit_width)
.value("actual_size", odr::HtmlViewportMode::actual_size)
.value("none", odr::HtmlViewportMode::none);

py::enum_<odr::PdfTextMode>(m, "PdfTextMode")
.value("dual_layer", odr::PdfTextMode::dual_layer)
.value("single_layer", odr::PdfTextMode::single_layer);
Expand Down Expand Up @@ -72,6 +78,10 @@ void odr_python::bind_html(py::module_ &m) {
&odr::HtmlConfig::spreadsheet_limit_by_content)
.def_readwrite("spreadsheet_gridlines",
&odr::HtmlConfig::spreadsheet_gridlines)
.def_readwrite("viewport_mode", &odr::HtmlConfig::viewport_mode)
.def_readwrite("spreadsheet_viewport_mode",
&odr::HtmlConfig::spreadsheet_viewport_mode)
.def_readwrite("viewport_content", &odr::HtmlConfig::viewport_content)
.def_readwrite("format_html", &odr::HtmlConfig::format_html)
.def_readwrite("html_indent", &odr::HtmlConfig::html_indent)
.def_readwrite("html_indent_string", &odr::HtmlConfig::html_indent_string)
Expand Down
44 changes: 44 additions & 0 deletions python/tests/test_html.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,50 @@ def test_html_config_defaults():
assert config.spreadsheet_limit.rows == 100


def test_html_config_viewport_defaults():
config = pyodr.HtmlConfig()
assert config.viewport_mode == pyodr.HtmlViewportMode.automatic
assert config.spreadsheet_viewport_mode is None
assert config.viewport_content is None

config.viewport_mode = pyodr.HtmlViewportMode.fit_width
config.spreadsheet_viewport_mode = pyodr.HtmlViewportMode.actual_size
config.viewport_content = "width=420"
assert config.viewport_mode == pyodr.HtmlViewportMode.fit_width
assert config.spreadsheet_viewport_mode == pyodr.HtmlViewportMode.actual_size
assert config.viewport_content == "width=420"


def test_viewport_mode_reaches_the_html(core_data_path, odt_path, tmp_path):
# The C++ suite covers the mode matrix; this only proves the config crosses
# the binding. A text document without margins is reflowing content, so
# `automatic` resolves to `actual_size`.
def render(name, config):
cache = tmp_path / name
cache.mkdir()
file = pyodr.open(str(odt_path))
service = pyodr.html.translate(file, str(cache), config)
content, _ = service.list_views()[0].write_html()
return content

assert (
'<meta name="viewport" '
'content="width=device-width,initial-scale=1.0,user-scalable=yes"/>'
in render("automatic", pyodr.HtmlConfig())
)

fit_width = pyodr.HtmlConfig()
fit_width.viewport_mode = pyodr.HtmlViewportMode.fit_width
assert (
'<meta name="viewport" content="width=device-width,user-scalable=yes"/>'
in render("fit_width", fit_width)
)

raw = pyodr.HtmlConfig()
raw.viewport_content = "width=420"
assert '<meta name="viewport" content="width=420"/>' in render("raw", raw)


def test_translate_text(core_data_path, txt_path, tmp_path):
html = translate_offline(txt_path, tmp_path)
pages = html.pages()
Expand Down
Loading