Skip to content

Commit

Permalink
Merge pull request #3953 from rism-digital/develop-scope-css
Browse files Browse the repository at this point in the history
Scope the CSS included in the SVG
  • Loading branch information
ahankinson authored Feb 17, 2025
2 parents b7df70e + f1dbc30 commit 82a88b2
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 12 deletions.
15 changes: 13 additions & 2 deletions include/vrv/svgdevicecontext.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class SvgDeviceContext : public DeviceContext {
* @name Constructors, destructors, and other standard methods
*/
///@{
SvgDeviceContext();
SvgDeviceContext(const std::string &docId);
virtual ~SvgDeviceContext();
///@}

Expand Down Expand Up @@ -233,7 +233,11 @@ class SvgDeviceContext : public DeviceContext {
/**
* Setter for an additional CSS
*/
void SetCss(const std::string &css) { m_css = css; }
void SetCss(const std::string &css)
{
m_css = css;
this->PrefixCssRules(m_css);
}

/**
* Copies additional attributes of defined elements to the SVG, each string in the form "elementName@attribute"
Expand Down Expand Up @@ -307,6 +311,11 @@ class SvgDeviceContext : public DeviceContext {
void AppendStrokeDashArray(pugi::xml_node node, const Pen &pen);
///@}

/**
* Prefix the CSS rules with a #docId for scoping them to the SVG
*/
void PrefixCssRules(std::string &rules);

public:
//
private:
Expand Down Expand Up @@ -384,6 +393,8 @@ class SvgDeviceContext : public DeviceContext {
std::string m_glyphPostfixId;
// embedding of the smufl text font
option_SMUFLTEXTFONT m_smuflTextFont;
// the document id
std::string m_docId;
};

} // namespace vrv
Expand Down
53 changes: 44 additions & 9 deletions src/svgdevicecontext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
//----------------------------------------------------------------------------

#include <cassert>
#include <regex>

//----------------------------------------------------------------------------

Expand All @@ -33,8 +34,10 @@ namespace vrv {
// SvgDeviceContext
//----------------------------------------------------------------------------

SvgDeviceContext::SvgDeviceContext() : DeviceContext(SVG_DEVICE_CONTEXT)
SvgDeviceContext::SvgDeviceContext(const std::string &docId) : DeviceContext(SVG_DEVICE_CONTEXT)
{
m_docId = docId;

m_originX = 0;
m_originY = 0;

Expand All @@ -59,8 +62,8 @@ SvgDeviceContext::SvgDeviceContext() : DeviceContext(SVG_DEVICE_CONTEXT)
m_svgNode.append_attribute("version") = "1.1";
m_svgNode.append_attribute("xmlns") = "http://www.w3.org/2000/svg";
m_svgNode.append_attribute("xmlns:xlink") = "http://www.w3.org/1999/xlink";
m_svgNode.append_attribute("xmlns:mei") = "http://www.music-encoding.org/ns/mei";
m_svgNode.append_attribute("overflow") = "visible";
m_svgNode.append_attribute("id") = m_docId;

// start the stack
m_svgNodeStack.push_back(m_svgNode);
Expand Down Expand Up @@ -472,19 +475,21 @@ void SvgDeviceContext::StartPage()
if (this->UseGlobalStyling()) {
m_currentNode = m_currentNode.append_child("style");
m_currentNode.append_attribute("type") = "text/css";
m_currentNode.text().set("g.page-margin{font-family:Times,serif;} "
//"g.page-margin{background: pink;} "
//"g.bounding-box{stroke:red; stroke-width:10} "
//"g.content-bounding-box{stroke:blue; stroke-width:10} "
"g.ending, g.fing, g.reh, g.tempo{font-weight:bold;} g.dir, g.dynam, "
"g.mNum{font-style:italic;} g.label{font-weight:normal;} path{stroke:currentColor}");
std::string css = "g.page-margin{font-family:Times,serif;} "
"g.ending, g.fing, g.reh, g.tempo{font-weight:bold;} g.dir, g.dynam, "
"g.mNum{font-style:italic;} g.label{font-weight:normal;} path{stroke:currentColor}";
// bounding box css - for debugging
// css += " g.bounding-box{stroke:red; stroke-width:10} "
// "g.content-bounding-box{stroke:blue; stroke-width:10}";
this->PrefixCssRules(css);
m_currentNode.text().set(css);
m_currentNode = m_svgNodeStack.back();
}

if (!m_css.empty()) {
m_currentNode = m_currentNode.append_child("style");
m_currentNode.append_attribute("type") = "text/css";
m_currentNode.text().set(m_css.c_str());
m_currentNode.text().set(m_css);
m_currentNode = m_svgNodeStack.back();
}

Expand Down Expand Up @@ -613,6 +618,36 @@ void SvgDeviceContext::AppendStrokeDashArray(pugi::xml_node node, const Pen &pen
}
}

void SvgDeviceContext::PrefixCssRules(std::string &rules)
{
static std::regex selectorRegex(R"(([^{}]+)\s*\{([^}]*)\})");
static std::regex multiSelectorRegex(R"((\b\w+\.?[\w-]+\b))");

std::sregex_iterator it(rules.begin(), rules.end(), selectorRegex);
std::sregex_iterator end;

std::string result;

while (it != end) {

std::string selectors = (*it)[1].str();
std::string properties = (*it)[2].str();

// Trim trailing spaces from selectors to prevent extra spaces before `{`
selectors = std::regex_replace(selectors, std::regex(R"(\s+$)"), "");

// Prepend `#docId` to each selector
selectors = std::regex_replace(selectors, multiSelectorRegex, "#" + m_docId + " $1");

// Keep contents inside `{}` unchanged
result += selectors + " {" + properties + "}";

++it;
}

rules = result;
}

// Drawing methods
void SvgDeviceContext::DrawQuadBezierPath(Point bezier[3])
{
Expand Down
2 changes: 1 addition & 1 deletion src/toolkit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1684,7 +1684,7 @@ std::string Toolkit::RenderToSVG(int pageNo, bool xmlDeclaration)
int initialPageNo = (m_doc.GetDrawingPage() == NULL) ? -1 : m_doc.GetDrawingPage()->GetIdx();
// Create the SVG object, h & w come from the system
// We will need to set the size of the page after having drawn it depending on the options
SvgDeviceContext svg;
SvgDeviceContext svg(m_doc.GetID());
svg.SetResources(&m_doc.GetResources());

int indent = (m_options->m_outputIndentTab.GetValue()) ? -1 : m_options->m_outputIndent.GetValue();
Expand Down

0 comments on commit 82a88b2

Please sign in to comment.