You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
src/odr/internal/svm/svm_to_svg.cpp is a ~270 line best-effort translator that covers a small slice of the metafile action set. It is the only thing standing between an embedded StarView metafile (charts, OLE replacement images in ODF/OOXML) and the HTML output — html::write_image_src calls it, and on any exception silently falls back to emitting the raw .svm bytes as a data URL, which no browser renders. So every gap here shows up as a blank or wrong image, never as an error.
This issue is the umbrella for making that converter good. #194 (bitmaps) and #95 (font attributes) are sub-parts of it.
Resources — what actually exists
Answering the obvious first question: there is no formal specification. But we are not limited to reading LibreOffice from scratch either.
1. ONLYOFFICE's reverse-engineered spec — the best starting point
A prose specification modelled on Microsoft's WMF/EMF spec documents: signature, header, primitive types, basic/graphics objects, and the actions split into control / state-changing / drawing. Self-described provenance: "As far as we know, there is no formal specification of this format. This documentation has been created by reading the source code of LibreOffice." It is incomplete — several FIXMEs, an "Unsorted Action Types" section, Color unfinished, and polygon flag handling explicitly called out as unfinished — but it covers considerably more than we implement today, and it is far cheaper to read than the LibreOffice sources.
Alongside it, an independent C++ reader/player we can cross-check against: SvmEnums.h, SvmObjects.{h,cpp}, SvmFile.{h,cpp}, SvmPlayer.{h,cpp} in the same directory.
2. LibreOffice — the ground truth, and it already contains a metafile→SVG writer
The single most valuable reference is not the reader but the writer: LibreOffice's SVG export filter walks a GDIMetaFile and emits SVG, i.e. exactly our problem, solved.
filter/source/svg/svgwriter.cxx — SVGActionWriter::ImplWriteActions, ~4.3k lines, handles 52 action types. Read this for the SVG mapping decisions (clip paths, gradients, text placement, the DX-array handling, transparency).
3. Other implementations, for sanity-checking only
andiwand/svm — our own earlier Java implementation.
SoftarexTechnologies/svmconv — MIT, JS, renders to bitmap-on-canvas rather than SVG, ~11 commits, dormant. Of limited use, and it has a telling hack (it rewrites black to white because it ignores transparency).
There is nothing else: the remaining search hits are file-extension directory sites and online converters that just shell out to LibreOffice.
TEXTRECT (parsed by read_text_rectangle_action, never called)
Other
EPS, COMMENT
Defects in what we already emit
Worth fixing before, or alongside, adding actions — these silently corrupt output we currently believe works:
Text is not XML-escaped.write_text does out << text straight into the SVG. Any &, < or > in a chart label produces a malformed document; the browser then renders nothing. html::escape_text already exists in internal/html/common.hpp. This is the cheapest high-impact fix here.
PUSH/POP are ignored, so the graphics state stack does not exist and colours/fonts set inside a push leak out of it and contaminate everything after. This is a whole-image correctness bug, not a missing feature.
POLYPOLYGON holes fill solid. We emit one <polygon> per sub-polygon; the format means them as one path with a fill rule, so donuts, rings and letter counters come out filled. Should be a single <path> with fill-rule.
Bézier flags are dropped.read_poly_line_action reads the has_flags byte and then // TODO flags not implemented; read_polygon has no flag concept at all. Every curve is currently rendered as a polyline through its control points, which is visibly wrong, not just approximate. (Acknowledged as hard — the ONLYOFFICE SPEC also flags it as unfinished.)
Font size is not transformed. Coordinates go through transform_x/transform_y, but write_text_style emits font-size: context.font.size.y raw, so text scales independently of the drawing whenever the map mode scale is not 1:1.
MapMode::unit is ignored. We use only origin and scale, so 100th-mm / twip / pixel units are all treated alike.
TEXTALIGN missing ⇒ everything is anchored at the baseline-left default; centre- and right-aligned labels (i.e. most chart labels) sit in the wrong place.
LineInfo is discarded.read_line_info fills width, dash/dot pattern and join, write_line_style uses none of it and hardcodes vector-effect:non-scaling-stroke.
write_style(out, context, int) dispatches on magic 0/1/2 with a // TODO log or throw default. Wants an enum.
Failures are invisible. Every unhandled action and every skipped byte carries a // TODO log; nothing is wired to Logger, and write_image_src swallows the exception path entirely. We cannot tell a file we rendered correctly from one we rendered as a blank rectangle.
Suggested order
Escaping (1), state stack (2), poly-polygon fill rule (3), logging (10) — small, self-contained, fix output we already produce.
Text correctness: TEXTALIGN, font-size transform, then the svm support #95 font attribute checklist (italic/bold/underline/strikeout).
The missing primitives (LINE, ELLIPSE, ROUNDRECT, ARC, PIE, CHORD, POINT, PIXEL) — mechanical, one svgwriter.cxx case each.
Clipping, then gradients/hatch, then transparency.
Béziers (4), and old version-1 SVM via SvmConverter.cxx, as stretch goals.
Test material already in tree: test/data/input/odr-public/svm/{chart-1,table-1}.svm, odr-private/svm/{test,Vyplaty}.svm, plus the odt/ods files named *svm*. Test coverage today is two smoke tests (svm_test.cpp) that only assert the output is non-empty — a real fix should come with reference-output comparison, and LibreOffice's own --convert-to svg makes a usable oracle.
src/odr/internal/svm/svm_to_svg.cppis a ~270 line best-effort translator that covers a small slice of the metafile action set. It is the only thing standing between an embedded StarView metafile (charts, OLE replacement images in ODF/OOXML) and the HTML output —html::write_image_srccalls it, and on any exception silently falls back to emitting the raw.svmbytes as a data URL, which no browser renders. So every gap here shows up as a blank or wrong image, never as an error.This issue is the umbrella for making that converter good. #194 (bitmaps) and #95 (font attributes) are sub-parts of it.
Resources — what actually exists
Answering the obvious first question: there is no formal specification. But we are not limited to reading LibreOffice from scratch either.
1. ONLYOFFICE's reverse-engineered spec — the best starting point
DesktopEditor/raster/Metafile/StarView/SPECA prose specification modelled on Microsoft's WMF/EMF spec documents: signature, header, primitive types, basic/graphics objects, and the actions split into control / state-changing / drawing. Self-described provenance: "As far as we know, there is no formal specification of this format. This documentation has been created by reading the source code of LibreOffice." It is incomplete — several FIXMEs, an "Unsorted Action Types" section,
Colorunfinished, and polygon flag handling explicitly called out as unfinished — but it covers considerably more than we implement today, and it is far cheaper to read than the LibreOffice sources.Alongside it, an independent C++ reader/player we can cross-check against:
SvmEnums.h,SvmObjects.{h,cpp},SvmFile.{h,cpp},SvmPlayer.{h,cpp}in the same directory.2. LibreOffice — the ground truth, and it already contains a metafile→SVG writer
The single most valuable reference is not the reader but the writer: LibreOffice's SVG export filter walks a
GDIMetaFileand emits SVG, i.e. exactly our problem, solved.filter/source/svg/svgwriter.cxx—SVGActionWriter::ImplWriteActions, ~4.3k lines, handles 52 action types. Read this for the SVG mapping decisions (clip paths, gradients, text placement, the DX-array handling, transparency).vcl/source/filter/svm/SvmReader.cxx— the authoritative binary layout, per action, per version.vcl/source/filter/svm/SvmConverter.cxx— the old (version 1, pre-VCLMTF) format, which we do not read at all.include/vcl/metaact.hxx— action semantics (already cited at the top of oursvm_format.hpp).vcl/source/bitmap/dibtools.cxx— DIB decoding, needed for svm support bitmaps #194. (Note: the path in svm support bitmaps #194 is stale, the file moved fromvcl/source/gdi/.)3. Other implementations, for sanity-checking only
andiwand/svm— our own earlier Java implementation.SoftarexTechnologies/svmconv— MIT, JS, renders to bitmap-on-canvas rather than SVG, ~11 commits, dormant. Of limited use, and it has a telling hack (it rewrites black to white because it ignores transparency).There is nothing else: the remaining search hits are file-extension directory sites and online converters that just shell out to LibreOffice.
Where we stand
Handled today — drawing:
RECT,POLYLINE,POLYGON,POLYPOLYGON,TEXT,TEXTARRAY,STRETCHTEXT. State:FILLCOLOR,LINECOLOR,OVERLINECOLOR,TEXTCOLOR,TEXTFILLCOLOR,FONT,TEXTLINE,MAPMODE.Every other action falls into
default:and is skipped. Against the 52 thatsvgwriter.cxxhandles, the notable misses are:PUSH,POP,TEXTALIGN,RASTEROP,REFPOINT,TEXTLINECOLOR,LAYOUTMODECLIPREGION,ISECTRECTCLIPREGION,ISECTREGIONCLIPREGION,MOVECLIPREGIONPIXEL,POINT,LINE,ROUNDRECT,ELLIPSE,ARC,PIE,CHORDBMP,BMPSCALE,BMPSCALEPART,BMPEX,BMPEXSCALE,BMPEXSCALEPART,MASK*GRADIENT,GRADIENTEX,HATCH,WALLPAPERTRANSPARENT,FLOATTRANSPARENTTEXTRECT(parsed byread_text_rectangle_action, never called)EPS,COMMENTDefects in what we already emit
Worth fixing before, or alongside, adding actions — these silently corrupt output we currently believe works:
write_textdoesout << textstraight into the SVG. Any&,<or>in a chart label produces a malformed document; the browser then renders nothing.html::escape_textalready exists ininternal/html/common.hpp. This is the cheapest high-impact fix here.PUSH/POPare ignored, so the graphics state stack does not exist and colours/fonts set inside a push leak out of it and contaminate everything after. This is a whole-image correctness bug, not a missing feature.POLYPOLYGONholes fill solid. We emit one<polygon>per sub-polygon; the format means them as one path with a fill rule, so donuts, rings and letter counters come out filled. Should be a single<path>withfill-rule.read_poly_line_actionreads thehas_flagsbyte and then// TODO flags not implemented;read_polygonhas no flag concept at all. Every curve is currently rendered as a polyline through its control points, which is visibly wrong, not just approximate. (Acknowledged as hard — the ONLYOFFICE SPEC also flags it as unfinished.)transform_x/transform_y, butwrite_text_styleemitsfont-size: context.font.size.yraw, so text scales independently of the drawing whenever the map mode scale is not 1:1.MapMode::unitis ignored. We use only origin and scale, so 100th-mm / twip / pixel units are all treated alike.TEXTALIGNmissing ⇒ everything is anchored at the baseline-left default; centre- and right-aligned labels (i.e. most chart labels) sit in the wrong place.LineInfois discarded.read_line_infofills width, dash/dot pattern and join,write_line_styleuses none of it and hardcodesvector-effect:non-scaling-stroke.write_style(out, context, int)dispatches on magic0/1/2with a// TODO log or throwdefault. Wants an enum.// TODO log; nothing is wired toLogger, andwrite_image_srcswallows the exception path entirely. We cannot tell a file we rendered correctly from one we rendered as a blank rectangle.Suggested order
TEXTALIGN, font-size transform, then the svm support #95 font attribute checklist (italic/bold/underline/strikeout).LINE,ELLIPSE,ROUNDRECT,ARC,PIE,CHORD,POINT,PIXEL) — mechanical, onesvgwriter.cxxcase each.SvmConverter.cxx, as stretch goals.Test material already in tree:
test/data/input/odr-public/svm/{chart-1,table-1}.svm,odr-private/svm/{test,Vyplaty}.svm, plus theodt/odsfiles named*svm*. Test coverage today is two smoke tests (svm_test.cpp) that only assert the output is non-empty — a real fix should come with reference-output comparison, and LibreOffice's own--convert-to svgmakes a usable oracle.