Skip to content

Commit 94d0d15

Browse files
committed
More support
1 parent 36d9605 commit 94d0d15

5 files changed

Lines changed: 115 additions & 5 deletions

File tree

Lines changed: 6 additions & 0 deletions
Loading

examples/graphics/source/examples/Svg.h

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,59 @@ class SvgDemo : public yup::Component
9999
demoFont = std::move (font);
100100
}
101101

102+
std::optional<yup::Image> fetchHttpImage (const yup::String& href)
103+
{
104+
if (! href.startsWithIgnoreCase ("http:") && ! href.startsWithIgnoreCase ("https:"))
105+
return std::nullopt;
106+
107+
if (httpImageCache.contains (href))
108+
return httpImageCache[href];
109+
110+
yup::MemoryBlock imageData;
111+
int statusCode = 0;
112+
113+
auto streamOptions = yup::URL::InputStreamOptions (yup::URL::ParameterHandling::inAddress)
114+
.withConnectionTimeoutMs (5000)
115+
.withNumRedirectsToFollow (5)
116+
.withStatusCode (&statusCode)
117+
.withExtraHeaders ("User-Agent: YUP SVG Demo\r\nAccept: image/*\r\n");
118+
119+
auto stream = yup::URL (href).createInputStream (streamOptions);
120+
if (stream == nullptr)
121+
{
122+
YUP_DBG ("Unable to fetch SVG image href: " << href);
123+
return std::nullopt;
124+
}
125+
126+
stream->readIntoMemoryBlock (imageData);
127+
128+
if ((statusCode != 0 && (statusCode < 200 || statusCode >= 300)) || imageData.isEmpty())
129+
{
130+
YUP_DBG ("Unable to fetch SVG image href: " << href << " status: " << statusCode);
131+
return std::nullopt;
132+
}
133+
134+
auto imageResult = yup::Image::loadFromData (imageData.asBytes());
135+
if (imageResult.failed())
136+
{
137+
YUP_DBG ("Unable to decode SVG image href: " << href << " error: " << imageResult.getErrorMessage());
138+
return std::nullopt;
139+
}
140+
141+
auto image = imageResult.getValue();
142+
httpImageCache.set (href, image);
143+
return image;
144+
}
145+
102146
yup::Drawable::ParseOptions createParseOptions (const yup::File& svgFile)
103147
{
104148
yup::Drawable::ParseOptions options;
105149
options.baseDirectory = svgFile.getParentDirectory();
150+
options.imageResolver = [this] (yup::StringRef href, const yup::File&) -> std::optional<yup::Image>
151+
{
152+
return fetchHttpImage (yup::String (href.text));
153+
};
154+
106155
options.fontResolver = [this] (yup::StringRef, float fontSize, int weight, bool italic) -> std::optional<yup::Font>
107156
{
108157
if (demoFont)
@@ -129,5 +178,6 @@ class SvgDemo : public yup::Component
129178
yup::Array<yup::File> svgFiles;
130179
yup::File dataDirectory;
131180
std::optional<yup::Font> demoFont;
181+
yup::HashMap<yup::String, yup::Image> httpImageCache;
132182
int currentSvgFileIndex = 0;
133183
};

modules/yup_graphics/drawables/yup_Drawable.cpp

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,10 +1054,10 @@ void Drawable::paintDebugElement (Graphics& g, const SVGElement& element)
10541054
Font Drawable::resolveFont (const SVGElement& element) const
10551055
{
10561056
if (document == nullptr)
1057-
return Font().withHeight (element.fontSize.value_or (12.0f));
1057+
return Font().withHeight (element.fontSize.value_or (16.0f));
10581058

10591059
const auto& options = document->getParseOptions();
1060-
const auto fontSize = element.fontSize.value_or (12.0f);
1060+
const auto fontSize = element.fontSize.value_or (16.0f);
10611061
const auto fontWeight = element.fontWeight.value_or (400);
10621062
const auto fontItalic = element.fontItalic.value_or (false);
10631063

@@ -1089,7 +1089,7 @@ void Drawable::renderTextElement (Graphics& g, const SVGElement& element)
10891089
position.setY (position.getY() + element.textDy->getFirst());
10901090

10911091
const auto font = resolveFont (element);
1092-
const auto fontSize = element.fontSize.value_or (12.0f);
1092+
const auto fontSize = element.fontSize.value_or (16.0f);
10931093

10941094
StyledText styledText;
10951095
{
@@ -1134,9 +1134,26 @@ void Drawable::renderImageElement (Graphics& g, const SVGElement& element)
11341134
if (! element.imageBounds)
11351135
return;
11361136

1137+
auto drawImage = [this, &g, &element] (const Image& image)
1138+
{
1139+
if (! image.isValid())
1140+
return;
1141+
1142+
const Rectangle<float> imageSourceBounds (0.0f, 0.0f, static_cast<float> (image.getWidth()), static_cast<float> (image.getHeight()));
1143+
const auto imageTransform = calculateTransformForTarget (imageSourceBounds,
1144+
*element.imageBounds,
1145+
element.preserveAspectRatioFitting,
1146+
element.preserveAspectRatioJustification);
1147+
const auto fittedImageBounds = imageSourceBounds.transformed (imageTransform);
1148+
1149+
const auto savedState = g.saveState();
1150+
g.setClipPath (*element.imageBounds);
1151+
g.drawImage (image, fittedImageBounds);
1152+
};
1153+
11371154
if (element.image)
11381155
{
1139-
g.drawImage (*element.image, *element.imageBounds);
1156+
drawImage (*element.image);
11401157
return;
11411158
}
11421159

@@ -1145,7 +1162,7 @@ void Drawable::renderImageElement (Graphics& g, const SVGElement& element)
11451162
if (document != nullptr)
11461163
{
11471164
if (auto image = SVGParser::loadImageFromHref (document->getParseOptions(), *element.imageHref))
1148-
g.drawImage (*image, *element.imageBounds);
1165+
drawImage (*image);
11491166
}
11501167
}
11511168
}

modules/yup_graphics/svg/yup_SVGParser.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,12 @@ bool SVGParser::parseElement (const XmlElement& element, bool parentIsRoot, Affi
471471

472472
e->imageBounds = Rectangle<float> (x, y, width, height);
473473

474+
if (auto preserveAspectRatio = element.getStringAttribute ("preserveAspectRatio"); preserveAspectRatio.isNotEmpty())
475+
{
476+
e->preserveAspectRatioFitting = parsePreserveAspectRatio (preserveAspectRatio);
477+
e->preserveAspectRatioJustification = parseAspectRatioAlignment (preserveAspectRatio);
478+
}
479+
474480
String href = element.getStringAttribute ("href");
475481
if (href.isEmpty())
476482
href = element.getStringAttribute ("xlink:href");

tests/yup_graphics/yup_SVGParser.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,37 @@ TEST (SVGParserTests, ParseImageWithMissingHref)
655655
EXPECT_TRUE (d.parseSVG ("<svg><image x=\"0\" y=\"0\" width=\"50\" height=\"50\" /></svg>"));
656656
}
657657

658+
TEST (SVGParserTests, ParseImageDefaultsToPreservingAspectRatio)
659+
{
660+
auto doc = SVGParser::parse ("<svg><image x=\"0\" y=\"0\" width=\"50\" height=\"25\" /></svg>");
661+
ASSERT_NE (nullptr, doc);
662+
663+
doc->visit ([] (const SVGData& data)
664+
{
665+
ASSERT_EQ (1u, data.elements.size());
666+
667+
const auto& image = *data.elements.front();
668+
EXPECT_EQ (Fitting::scaleToFit, image.preserveAspectRatioFitting);
669+
EXPECT_TRUE (image.preserveAspectRatioJustification.testFlags (Justification::center));
670+
});
671+
}
672+
673+
TEST (SVGParserTests, ParseImagePreserveAspectRatioAttribute)
674+
{
675+
auto doc = SVGParser::parse ("<svg><image x=\"0\" y=\"0\" width=\"50\" height=\"25\" preserveAspectRatio=\"xMaxYMin slice\" /></svg>");
676+
ASSERT_NE (nullptr, doc);
677+
678+
doc->visit ([] (const SVGData& data)
679+
{
680+
ASSERT_EQ (1u, data.elements.size());
681+
682+
const auto& image = *data.elements.front();
683+
EXPECT_EQ (Fitting::scaleToFill, image.preserveAspectRatioFitting);
684+
EXPECT_TRUE (image.preserveAspectRatioJustification.testFlags (Justification::right));
685+
EXPECT_TRUE (image.preserveAspectRatioJustification.testFlags (Justification::top));
686+
});
687+
}
688+
658689
// ==============================================================================
659690
// Gradients
660691
// ==============================================================================

0 commit comments

Comments
 (0)