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
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ bytes ─▢ magic/open_strategy ─▢ DecodedFile ─▢ Document ─▢ Eleme
| `src/odr/internal/markdown/` | Markdown (CommonMark + GFM via md4c), decoded to a text document; see [`markdown/AGENTS.md`](src/odr/internal/markdown/AGENTS.md) + [`markdown/PLAN.md`](src/odr/internal/markdown/PLAN.md). |
| `src/odr/internal/xml/` | XML: the pugixml parse and the escaping every xml-writing engine shares (`xml_util`), plus the source view (`xml_file`); see [`xml/AGENTS.md`](src/odr/internal/xml/AGENTS.md). |
| `src/odr/internal/svg/` | SVG, detected by reading it as xml; see [`svg/AGENTS.md`](src/odr/internal/svg/AGENTS.md). |
| `src/odr/internal/svm/` | StarView metafile, the vector image odf/ooxml packages carry for charts and OLE objects; translated to svg. See [`svm/AGENTS.md`](src/odr/internal/svm/AGENTS.md) + [`svm/PLAN.md`](src/odr/internal/svm/PLAN.md). |
| `src/odr/internal/svm/` | StarView metafile, the vector image odf/ooxml packages carry for charts and OLE objects; translated to svg. See [`svm/AGENTS.md`](src/odr/internal/svm/AGENTS.md). |
| `src/odr/internal/{csv,json,text}/` | Smaller formats. |
| `cli/src/` | CLI tools: `translate`, `back_translate`, `meta`, `server`. |
| `python/` | Python bindings (`pyodr`, pybind11); see [`python/AGENTS.md`](python/AGENTS.md). |
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ The release run heads these entries with the version and opens a fresh

## Unreleased

- A StarView metafile's text is decoded by the charset it names. Every
encoding but `UCS2` used to emit the file's own bytes, and the invalid utf-8
a non-ascii label made of that cost the whole image.

- A curve in a StarView metafile is drawn as a curve. The polygon flags that
say which of its points are bezier control points were read and dropped, so
every curve came out as a line through them.
Expand Down
43 changes: 39 additions & 4 deletions src/odr/internal/svm/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Read the root [`AGENTS.md`](../../../../AGENTS.md) first. This file covers what
svm does differently, and why. [`README.md`](README.md) has the feature matrix
and the references, [`PLAN.md`](PLAN.md) the roadmap.
and the references.

## What it is

Expand Down Expand Up @@ -62,9 +62,44 @@ The format is not specified anywhere. The references, best first:
unescaped `&` costs the whole image, not one label. Note that
`html::escape_text` is the *wrong* escape here β€” it emits ` `, which no
xml parser knows.
- **Escaping is not yet enough.** `read_string_with_encoding` hands back the
file's own bytes for every encoding but `UCS2`, so a latin-1 label emits
invalid utf-8 and the parser refuses the document all the same.
- **Decoding is the other half of escaping.** A byte string is decoded by the
charset the last `FONT` action named, because handing the bytes through
emits invalid utf-8 for anything but ascii and an xml parser refuses that
exactly as hard as an unescaped `&`. An encoding we have no decoder for is
taken for `MS_1252` rather than passed through.

## What the corpus holds

Over 1125 metafiles harvested from the `odt`/`ods` fixtures, which is what
every "occurs nowhere" in this module is measured against:

| action | occurrences | files (of 1125) |
| --- | --- | --- |
| `PUSH` / `POP` | 22317 each | 1124 |
| `TEXTALIGN` | 21534 | 1117 |
| `STRETCHTEXT` | 20335 | 1097 |
| `ISECTRECTCLIPREGION` | 1124 | 1123 |
| `RECT` | 845 | 324 |
| `TEXTARRAY` | 764 | 20 |
| `POLYLINE` | 477 | 13 |
| `POLYPOLYGON` | 247 | 14 |
| `LINE` | 47 | 2 |
| `BMPEXSCALE` | 1 | 1 |

Two thirds of every action is text, and half of that text is italic β€” all of
it a formula variable. `ELLIPSE`, `ARC`, `PIE`, `CHORD`, `ROUNDRECT`, `POINT`,
`PIXEL`, `GRADIENT`, `HATCH`, `TRANSPARENT` and `EPS` do not occur at all, nor
does a polygon flag or a complex poly-polygon. The one real bitmap is the
whole data area of `odr-private/svm/Vyplaty.svm` β€” 1.59 MB of `BMPEXSCALE` in
a 1.63 MB file.

So a change here is proved by `svm_test.cpp` and by LibreOffice, not by the
reference output: most of what the module now draws, no fixture exercises.

## `FLOATTRANSPARENT` nests a whole metafile

The one unimplemented action whose shape is already worked out: translate the
nested metafile into a `<g>` and put the gradient on that group's `mask`.

## Bitmaps do not go through a decoder

Expand Down
149 changes: 0 additions & 149 deletions src/odr/internal/svm/PLAN.md

This file was deleted.

78 changes: 44 additions & 34 deletions src/odr/internal/svm/README.md
Original file line number Diff line number Diff line change
@@ -1,53 +1,61 @@
# SVM implementation

StarView Metafile β†’ SVG. See [`AGENTS.md`](AGENTS.md) for how the module is
built and [`PLAN.md`](PLAN.md) for the order the gaps below get closed in.
built, what it does differently, and what the corpus actually contains.

## Features

38 of the 54 action types we name are drawn.

- [x] shapes
- [x] rectangle
- [x] polyline, polygon, poly-polygon
- [ ] fill rule of a poly-polygon (holes are painted over)
- [ ] pixel, point, line, rounded rectangle, ellipse, arc, pie, chord
- [ ] bΓ©zier segments (the polygon flags are not read)
- [x] rectangle, rounded rectangle
- [x] polyline, polygon, poly-polygon, with the fill rule that cuts its holes
- [x] pixel, point, line, ellipse, arc, pie, chord
- [x] bΓ©zier segments
- [x] colour
- [x] line, fill, text
- [ ] `LineInfo` (width, dash, join, cap)
- [x] `LineInfo` (width, dash, join)
- [ ] text fill, overline (read into the state, never drawn)
- [ ] font
- [x] size (in map-mode units, which the transform does not apply)
- [ ] italic, bold
- [ ] alignment
- [ ] underline, strike through
- [x] colour
- [x] family
- [x] font
- [x] family, size, colour
- [x] italic, bold, underline, strike through, rotation
- [x] alignment β€” `TextAlign` is vertical only, a run always starts at the
point
- [x] text
- [x] `TEXT`, `TEXTARRAY`, `STRETCHTEXT` as plain text at a point
- [ ] the `TEXTARRAY` dx array, `STRETCHTEXT` width, `TEXTRECT`
- [ ] non-`UCS2` encodings (the bytes go out undecoded, see below)
- [ ] transform (e.g. flip, rotate)
- [x] map mode origin and scale
- [ ] map mode unit
- [ ] images (`BMP`, `BMPEX`, `MASK` and their scale/part variants)
- [ ] gradient, hatch, wallpaper
- [ ] clipping regions
- [ ] transparency (`TRANSPARENT`, `FLOATTRANSPARENT`)
- [ ] graphics state stack (`PUSH`/`POP`)
- [x] `TEXT`, `TEXTARRAY`, `STRETCHTEXT`, and the run each of them names
- [x] the `TEXTARRAY` dx array and the `STRETCHTEXT` width
- [x] non-`UCS2` encodings, decoded to utf-8
- [ ] `TEXTRECT`, `TEXTLINE` β€” the reader has both, nothing draws them
- [x] map mode
- [x] origin, scale, unit
- [x] the relative map mode, which composes with the one before it
- [x] images
- [x] `BMP`, `BMPEX` and their scale and part variants, transparency mask
included
- [ ] the `MASK` family, which stencils one colour through a bitmap
- [ ] `ZCOMPRESS`ed dibs, whose zlib stream would have to be inflated first
- [x] gradient, hatch, transparency
- [x] `GRADIENT`, `GRADIENTEX`, `HATCH`, `TRANSPARENT`
- [ ] `WALLPAPER`, which has a format of its own
- [ ] `FLOATTRANSPARENT`, which nests a whole metafile
- [x] clipping
- [x] `CLIPREGION`, `ISECTRECTCLIPREGION`, `ISECTREGIONCLIPREGION`
- [ ] `MOVECLIPREGION`, which would have to move path data already written
- [x] graphics state stack (`PUSH`/`POP`), restoring only what the push named
- [ ] `EPS` substitute metafile
- [ ] version 1 (pre-`VCLMTF`) files
- [x] output is escaped, so a `&` in a label cannot cost the whole image
- [ ] version 1 (pre-`VCLMTF`) files, via `SvmConverter.cxx`
- [x] output is escaped and decoded, so no label can cost the whole image
- [x] every action we skip is logged by name

Anything not implemented is skipped by the action's own length, so the actions
after it still read.
Anything unimplemented is skipped by the action's own length, so the actions
after it still read. None of it occurs in the corpus.

### Known defect
### Where the drawing is approximate

Text in a non-`UCS2` encoding is passed through as the bytes the file holds
(`read_ascii_string`). A latin-1 label therefore emits invalid utf-8, and an
xml parser refuses that exactly as hard as an unescaped `&`. Escaping alone
does not make every label safe.
- A `SQUARE` or `RECT` gradient shrinks a rectangle rather than an ellipse,
which svg has no gradient for; it comes out as the ellipse closest to it.
- A triple hatch's third line set runs corner to corner of the pattern tile,
so those lines sit `distance / √2` apart rather than `distance`.

## References

Expand All @@ -61,6 +69,8 @@ does not make every label safe.
- [`svgwriter.cxx`](https://github.com/LibreOffice/core/blob/master/filter/source/svg/svgwriter.cxx)
β€” LibreOffice's own metafile β†’ svg export, i.e. our problem already solved.
The reference for mapping decisions.
- [`textenc.h`](https://github.com/LibreOffice/core/blob/master/include/rtl/textenc.h)
β€” the `rtl_TextEncoding` numbers a font's charset is one of.
- [`SPEC`](https://github.com/ONLYOFFICE/core/blob/master/DesktopEditor/raster/Metafile/StarView/SPEC)
β€” ONLYOFFICE's prose write-up, modelled on [MS-WMF]. Cheap to read, but
incomplete: several FIXMEs, `Color` and the polygon flags unfinished.
Expand Down
Loading
Loading