Skip to content

Commit 3885d53

Browse files
committed
Fallback to block parsing when loading rm files
1 parent 7c979c7 commit 3885d53

3 files changed

Lines changed: 85 additions & 7 deletions

File tree

rmfiles/remarkable.py

Lines changed: 81 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@
1717
MigrationInfoBlock,
1818
PageInfoBlock,
1919
RootTextBlock,
20+
SceneGlyphItemBlock,
21+
SceneLineItemBlock,
22+
SceneTreeBlock,
23+
TreeNodeBlock,
24+
read_blocks,
25+
read_tree,
2026
)
2127
from rmscene.scene_tree import SceneTree
2228
from rmscene.tagged_block_common import CrdtId, LwwValue
@@ -146,12 +152,16 @@ def from_file(
146152
) -> RemarkableNotebook:
147153
"""Load a notebook from an existing `.rm` file."""
148154

149-
from rmscene.scene_stream import read_tree
150-
151155
notebook = cls(version=version, deg=deg)
152-
with Path(path).open("rb") as handle:
153-
tree = read_tree(handle)
154-
notebook._load_from_tree(tree)
156+
path = Path(path)
157+
try:
158+
with path.open("rb") as handle:
159+
tree = read_tree(handle)
160+
notebook._load_from_tree(tree)
161+
except ValueError:
162+
with path.open("rb") as handle:
163+
blocks = list(read_blocks(handle))
164+
notebook._load_from_blocks(blocks)
155165
return notebook
156166

157167
def _load_from_tree(self, tree: SceneTree) -> None:
@@ -200,6 +210,72 @@ def _load_from_tree(self, tree: SceneTree) -> None:
200210
]
201211
highlights_entry.append((value.text or "", value.color, rects))
202212

213+
def _load_from_blocks(self, blocks: Iterable[Block]) -> None:
214+
self._lines.clear()
215+
self._highlights.clear()
216+
self._layer_visibility.clear()
217+
218+
layer_data: dict[CrdtId, dict[str, Any]] = {}
219+
order: list[CrdtId] = []
220+
221+
def entry(node_id: CrdtId) -> dict[str, Any]:
222+
if node_id not in layer_data:
223+
layer_data[node_id] = {
224+
"name": "",
225+
"visible": True,
226+
"lines": [],
227+
"highlights": [],
228+
}
229+
order.append(node_id)
230+
return layer_data[node_id]
231+
232+
for block in blocks:
233+
if isinstance(block, SceneTreeBlock):
234+
entry(block.tree_id)
235+
elif isinstance(block, TreeNodeBlock):
236+
group = block.group
237+
if isinstance(group, si.Group):
238+
data = entry(group.node_id)
239+
name = _extract_lww(group.label)
240+
if isinstance(name, str) and name:
241+
data["name"] = name
242+
visible = _extract_lww(group.visible)
243+
if visible is not None:
244+
data["visible"] = bool(visible)
245+
elif isinstance(block, SceneLineItemBlock):
246+
data = entry(block.parent_id)
247+
data["lines"].append(block.item.value)
248+
elif isinstance(block, SceneGlyphItemBlock):
249+
data = entry(block.parent_id)
250+
data["highlights"].append(block.item.value)
251+
252+
for idx, node_id in enumerate(order, start=1):
253+
data = layer_data[node_id]
254+
name = data["name"] or f"Layer {idx}"
255+
visible = bool(data.get("visible", True))
256+
self.layer(name, visible=visible)
257+
lines_entry = self._lines.setdefault(name, [])
258+
highlights_entry = self._highlights.setdefault(name, [])
259+
lines_entry.clear()
260+
highlights_entry.clear()
261+
262+
for line in data["lines"]:
263+
tool = Tool(
264+
pen=line.tool,
265+
color=line.color,
266+
width=int(max((pt.width for pt in line.points), default=2)),
267+
pressure=int(max((pt.pressure for pt in line.points), default=100)),
268+
thickness_scale=line.thickness_scale,
269+
)
270+
lines_entry.append((list(line.points), tool, self._affine))
271+
272+
for highlight in data["highlights"]:
273+
rects = [
274+
si.Rectangle(x=r.x, y=r.y, w=r.w, h=r.h)
275+
for r in highlight.rectangles
276+
]
277+
highlights_entry.append((highlight.text or "", highlight.color, rects))
278+
203279
# --- Layer management ---
204280
def layer(self, name: str, *, visible: bool = True) -> RemarkableNotebook:
205281
# visible is currently unused; reserved for future compile mapping

tests/test_cli.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,5 +78,6 @@ def test_cli_svg_export(tmp_path):
7878

7979
rc = _cmd_svg(args)
8080
assert rc == 0
81-
assert out.exists()
82-
assert "<svg" in out.read_text()
81+
data = out.read_text()
82+
assert "<svg" in data
83+
assert "viewBox" in data

tests/test_svg.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ def test_scene_to_svg_writes_paths_and_highlights(tmp_path):
2929
first_group = groups[0]
3030
title = first_group.find(_ns("title"))
3131
assert title is not None and title.text == "Sketch"
32+
assert "viewBox" in root.attrib
3233

3334
paths = first_group.findall(_ns("path"))
3435
assert paths and paths[0].attrib.get("stroke") == "#1976d2"

0 commit comments

Comments
 (0)