Here is a JavaScript-based solution. You call recordSVGFontSizes()
once at the beginning, passing it an SVG element and a StyleSheet. It calculates the viewport for the SVG, and runs through the stylesheet and finds all rules that mention font-size
and record the original font size.
Then, when your viewBox changes, pass the recorded values to normalizeSVGFontSizes()
. It will re-calculate the viewport and update all font sizes appropriately.
Note that since the viewport calculation does not currently work for Firefox, this script does not work there.
Demo: http://jsfiddle.net/CphxH/8/
// Do this once at startup
var recording = recordSVGFontSizes( mySVG, document.styleSheets[0] );
// Do this each time the viewport changes
normalizeSVGFontSizes(recording);
function recordSVGFontSizes(svgElement,styleSheet){
var rules = [];
var viewport = calculateViewport(svgElement);
for (var i=styleSheet.cssRules.length;i--;){
var rule = styleSheet.cssRules[i];
if (rule.style.fontSize){
var parts = rule.style.fontSize.split(/(\D+)/);
rules.push({ rule:rule, units:parts[1], perpx:parts[0] / viewport.width });
}
}
return {rules:rules, svg:svgElement};
}
function normalizeSVGFontSizes(fontSizeRecording){
var viewport = calculateViewport(fontSizeRecording.svg);
for (var i=fontSizeRecording.rules.length;i--;){
var record = fontSizeRecording.rules[i];
record.rule.style.fontSize = record.perpx*viewport.width + record.units;
}
}
// Given an <svg> element, returns an object with the visible bounds
// expressed in local viewBox units, e.g.
// { x:-50, y:-50, width:100, height:100 }
function calculateViewport(svg){ // http://phrogz.net/JS/_ReuseLicense.txt
var outer = svg.getBoundingClientRect();
var aspect = svg.preserveAspectRatio.baseVal,
viewBox = svg.viewBox.baseVal,
width = viewBox && viewBox.width || outer.width,
height = viewBox && viewBox.height || outer.height,
x = viewBox ? viewBox.x : 0,
y = viewBox ? viewBox.y : 0;
if (!width || !height || !outer.width) return;
if (aspect.align==aspect.SVG_PRESERVEASPECTRATIO_NONE || !viewBox || !viewBox.height){
return {x:x,y:y,width:width,height:height};
}else{
var inRatio = viewBox.width / viewBox.height,
outRatio = outer.width / outer.height;
var meetFlag = aspect.meetOrSlice != aspect.SVG_MEETORSLICE_SLICE;
var fillAxis = outRatio>inRatio ? (meetFlag?'y':'x') : (meetFlag?'x':'y');
if (fillAxis=='x'){
height = width/outRatio;
var diff = viewBox.height - height;
switch (aspect.align){
case aspect.SVG_PRESERVEASPECTRATIO_UNKNOWN:
case aspect.SVG_PRESERVEASPECTRATIO_XMINYMID:
case aspect.SVG_PRESERVEASPECTRATIO_XMIDYMID:
case aspect.SVG_PRESERVEASPECTRATIO_XMAXYMID:
y += diff/2;
break;
case aspect.SVG_PRESERVEASPECTRATIO_XMINYMAX:
case aspect.SVG_PRESERVEASPECTRATIO_XMIDYMAX:
case aspect.SVG_PRESERVEASPECTRATIO_XMAXYMAX:
y += diff;
break;
}
}
else{
width = height*outRatio;
var diff = viewBox.width - width;
switch (aspect.align){
case aspect.SVG_PRESERVEASPECTRATIO_UNKNOWN:
case aspect.SVG_PRESERVEASPECTRATIO_XMIDYMIN:
case aspect.SVG_PRESERVEASPECTRATIO_XMIDYMID:
case aspect.SVG_PRESERVEASPECTRATIO_XMIDYMAX:
x += diff/2;
break;
case aspect.SVG_PRESERVEASPECTRATIO_XMAXYMID:
case aspect.SVG_PRESERVEASPECTRATIO_XMAXYMIN:
case aspect.SVG_PRESERVEASPECTRATIO_XMAXYMAX:
x += diff;
break;
}
}
return {x:x,y:y,width:width,height:height};
}
}