svg-flatten: Fix parsing of layer groups in recent usvg
Recent usvg versions introduce an unnamed parent group at root level when the input SVG's viewBox and width/height attributes mismatch. We now deal with both this situation and with usvg's old behavior correctly.
This commit is contained in:
parent
71531021b9
commit
095ea10ee8
2 changed files with 33 additions and 19 deletions
|
|
@ -234,7 +234,8 @@ namespace gerbolyze {
|
|||
RenderContext(RenderContext &parent,
|
||||
xform2d transform,
|
||||
ClipperLib::Paths &clip,
|
||||
bool included);
|
||||
bool included,
|
||||
bool seen_id);
|
||||
RenderContext(RenderContext &parent,
|
||||
PolygonSink &sink,
|
||||
ClipperLib::Paths &clip);
|
||||
|
|
@ -243,21 +244,23 @@ namespace gerbolyze {
|
|||
const ElementSelector &sel() { return m_sel; }
|
||||
const RenderSettings &settings() { return m_settings; }
|
||||
xform2d &mat() { return m_mat; }
|
||||
bool root() const { return m_root; }
|
||||
int level() const { return m_level; }
|
||||
bool has_seen_id() const { return m_seen_id; }
|
||||
bool included() const { return m_included; }
|
||||
ClipperLib::Paths &clip() { return m_clip; }
|
||||
void transform(xform2d &transform) {
|
||||
m_mat.transform(transform);
|
||||
}
|
||||
bool match(const pugi::xml_node &node) {
|
||||
return m_sel.match(node, m_root, m_included);
|
||||
return m_sel.match(node, m_level < 2 && !m_seen_id, m_included);
|
||||
}
|
||||
|
||||
private:
|
||||
PolygonSink &m_sink;
|
||||
const RenderSettings &m_settings;
|
||||
xform2d m_mat;
|
||||
bool m_root;
|
||||
int m_level;
|
||||
bool m_seen_id;
|
||||
bool m_included; /* TODO: refactor name */
|
||||
const ElementSelector &m_sel;
|
||||
ClipperLib::Paths &m_clip;
|
||||
|
|
|
|||
|
|
@ -206,25 +206,32 @@ void gerbolyze::SVGDocument::export_svg_group(RenderContext &ctx, const pugi::xm
|
|||
for (const auto &node : group.children()) {
|
||||
string name(node.name());
|
||||
bool match = ctx.match(node);
|
||||
RenderContext elem_ctx(ctx, xform2d(node.attribute("transform").value()), clip_path, match);
|
||||
|
||||
if (name == "g") {
|
||||
if (ctx.root()) { /* Treat top-level groups as "layers" like inkscape does. */
|
||||
/* We treat as the "layer" the first group in the hierarchy's first two levels that we find that has an ID set.
|
||||
*
|
||||
* This is due to a recent change in usvg, where usvg sometimes introduces an unnamed top-level parent group
|
||||
* with a transform matrix to calculate out mismatching viewBox and width/height attributes on the input SVG
|
||||
` root element. */
|
||||
if (!ctx.has_seen_id() && ctx.level() < 2 && node.attribute("id")) {
|
||||
RenderContext elem_ctx(ctx, xform2d(node.attribute("transform").value()), clip_path, match, true);
|
||||
LayerNameToken tok { node.attribute("id").value() };
|
||||
elem_ctx.sink() << tok;
|
||||
}
|
||||
|
||||
export_svg_group(elem_ctx, node);
|
||||
|
||||
if (ctx.root()) {
|
||||
LayerNameToken tok {""};
|
||||
elem_ctx.sink() << tok;
|
||||
export_svg_group(elem_ctx, node);
|
||||
|
||||
elem_ctx.sink() << LayerNameToken {""};
|
||||
|
||||
} else {
|
||||
RenderContext elem_ctx(ctx, xform2d(node.attribute("transform").value()), clip_path, match, ctx.has_seen_id());
|
||||
export_svg_group(elem_ctx, node);
|
||||
}
|
||||
|
||||
} else if (name == "path") {
|
||||
if (!match)
|
||||
continue;
|
||||
|
||||
RenderContext elem_ctx(ctx, xform2d(node.attribute("transform").value()), clip_path, match, ctx.has_seen_id());
|
||||
export_svg_path(elem_ctx, node);
|
||||
|
||||
} else if (name == "image") {
|
||||
|
|
@ -237,6 +244,7 @@ void gerbolyze::SVGDocument::export_svg_group(RenderContext &ctx, const pugi::xm
|
|||
continue;
|
||||
}
|
||||
|
||||
RenderContext elem_ctx(ctx, xform2d(node.attribute("transform").value()), clip_path, match, ctx.has_seen_id());
|
||||
double min_feature_size_px = mm_to_doc_units(ctx.settings().m_minimum_feature_size_mm);
|
||||
vec->vectorize_image(elem_ctx, node, min_feature_size_px);
|
||||
delete vec;
|
||||
|
|
@ -362,7 +370,7 @@ void gerbolyze::SVGDocument::export_svg_path(RenderContext &ctx, const pugi::xml
|
|||
|
||||
} else {
|
||||
PolyTreeToPaths(ptree_fill, fill_paths);
|
||||
RenderContext local_ctx(ctx, xform2d(), fill_paths, true);
|
||||
RenderContext local_ctx(ctx, xform2d(), fill_paths, true, ctx.has_seen_id());
|
||||
pattern->tile(local_ctx);
|
||||
}
|
||||
|
||||
|
|
@ -552,7 +560,7 @@ void gerbolyze::SVGDocument::export_svg_path(RenderContext &ctx, const pugi::xml
|
|||
PolyTreeToPaths(ptree, clip);
|
||||
ctx.mat().doc2phys_clipper(clip);
|
||||
|
||||
RenderContext local_ctx(ctx, xform2d(), clip, true);
|
||||
RenderContext local_ctx(ctx, xform2d(), clip, true, ctx.has_seen_id());
|
||||
pattern->tile(local_ctx);
|
||||
}
|
||||
|
||||
|
|
@ -671,7 +679,8 @@ gerbolyze::RenderContext::RenderContext(const RenderSettings &settings,
|
|||
m_sink(sink),
|
||||
m_settings(settings),
|
||||
m_mat(),
|
||||
m_root(true),
|
||||
m_level(0),
|
||||
m_seen_id(false),
|
||||
m_included(false),
|
||||
m_sel(sel),
|
||||
m_clip(clip)
|
||||
|
|
@ -679,15 +688,16 @@ gerbolyze::RenderContext::RenderContext(const RenderSettings &settings,
|
|||
}
|
||||
|
||||
gerbolyze::RenderContext::RenderContext(RenderContext &parent, xform2d transform) :
|
||||
RenderContext(parent, transform, parent.clip(), parent.included())
|
||||
RenderContext(parent, transform, parent.clip(), parent.included(), parent.has_seen_id())
|
||||
{
|
||||
}
|
||||
|
||||
gerbolyze::RenderContext::RenderContext(RenderContext &parent, xform2d transform, ClipperLib::Paths &clip, bool included) :
|
||||
gerbolyze::RenderContext::RenderContext(RenderContext &parent, xform2d transform, ClipperLib::Paths &clip, bool included, bool seen_id) :
|
||||
m_sink(parent.sink()),
|
||||
m_settings(parent.settings()),
|
||||
m_mat(parent.mat()),
|
||||
m_root(false),
|
||||
m_level(parent.level() + 1),
|
||||
m_seen_id(seen_id),
|
||||
m_included(included),
|
||||
m_sel(parent.sel()),
|
||||
m_clip(clip)
|
||||
|
|
@ -699,7 +709,8 @@ gerbolyze::RenderContext::RenderContext(RenderContext &parent, PolygonSink &sink
|
|||
m_sink(sink),
|
||||
m_settings(parent.settings()),
|
||||
m_mat(parent.mat()),
|
||||
m_root(false),
|
||||
m_level(parent.level() + 1),
|
||||
m_seen_id(parent.has_seen_id()),
|
||||
m_included(true),
|
||||
m_sel(parent.sel()),
|
||||
m_clip(clip)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue