对xml文本进行处理

@param  {string} svg svg文本
@return {string} 处理后文本
170876
function resolveSVG(svg) {
            // 去除xmlns，防止xmlns导致svg解析错误
            svg = svg.replace(/\s+xmlns(?:\:[\w-]+)?=("|')[^"']*\1/g, ' ')
                .replace(/<defs[>\s][\s\S]+?\/defs>/g, function (text) {
                    if (text.indexOf('</font>') >= 0) {
                        return text;
                    }
                    return '';
                })
                .replace(/<use[>\s][\s\S]+?\/use>/g, '');
            return svg;
        }
1255492
def wsvg(paths=None, colors=None,
          filename=os_path.join(getcwd(), 'disvg_output.svg'),
          stroke_widths=None, nodes=None, node_colors=None, node_radii=None,
          openinbrowser=False, timestamp=False,
          margin_size=0.1, mindim=600, dimensions=None,
          viewbox=None, text=None, text_path=None, font_size=None,
          attributes=None, svg_attributes=None, svgwrite_debug=False, paths2Drawing=False):
    """"""
    disvg(paths, colors=colors, filename=filename,
          stroke_widths=stroke_widths, nodes=nodes,
          node_colors=node_colors, node_radii=node_radii,
          openinbrowser=openinbrowser, timestamp=timestamp,
          margin_size=margin_size, mindim=mindim, dimensions=dimensions,
          viewbox=viewbox, text=text, text_path=text_path, font_size=font_size,
          attributes=attributes, svg_attributes=svg_attributes,
          svgwrite_debug=svgwrite_debug, paths2Drawing=paths2Drawing)
166036
@SuppressWarnings({"WeakerAccess", "unused"})
   public RectF  getDocumentViewBox()
   {
      if (this.rootElement == null)
         throw new IllegalArgumentException("SVG document is empty");

      if (this.rootElement.viewBox == null)
         return null;

      return this.rootElement.viewBox.toRectF();       
   }
1280868
async function buildPathsObject(files) {
  return Promise.all(
    files.map(async filepath => {
      const name = path.basename(filepath, '.svg');
      const svg = fs.readFileSync(filepath, 'utf-8');
      const pathStrings = await svgo.optimize(svg, { path: filepath })
        .then(({ data }) => data.match(/ d="[^"]+"/g) || [])
        .then(paths => paths.map(s => s.slice(3)));
      return `"${name}": [${pathStrings.join(',\n')}]`;
    }),
  );
}
1191169
function svgText()
	{
		// $tmp = count($this->txt_style)-1;
		$current_style = $this->txt_style[count($this->txt_style) - 1]; // mPDF 5.7.4
		$style = '';
		$op = '';
		$render = -1;
		if (isset($this->txt_data[2])) {
			// mPDF 6
			// If using SVG Font
			if (isset($this->svg_font[$current_style['font-family']])) {
				// select font
				$style = 'R';
				$style .= (isset($current_style['font-weight']) && $current_style['font-weight'] == 'bold') ? 'B' : '';
				$style .= (isset($current_style['font-style']) && $current_style['font-style'] == 'italic') ? 'I' : '';
				$style .= (isset($current_style['font-variant']) && $current_style['font-variant'] == 'small-caps') ? 'S' : '';

				$fontsize = $current_style['font-size'] * $this->mpdf->dpi / 72;
				if (isset($this->svg_font[$current_style['font-family']][$style])) {
					$svg_font = $this->svg_font[$current_style['font-family']][$style];
				} else if (isset($this->svg_font[$current_style['font-family']]['R'])) {
					$svg_font = $this->svg_font[$current_style['font-family']]['R'];
				}

				if (!isset($svg_font['units-per-em']) || $svg_font['units-per-em'] < 1) {
					$svg_font['units-per-em'] = 1000;
				}
				$units_per_em = $svg_font['units-per-em'];
				$scale = $fontsize / $units_per_em;
				$stroke_width = $current_style['stroke-width'];
				$stroke_width /= $scale;

				$opacitystr = '';
				$fopacity = 1;
				if (isset($current_style['fill-opacity'])) {
					if ($current_style['fill-opacity'] == 0) {
						$fopacity = 0;
					} else if ($current_style['fill-opacity'] > 1) {
						$fopacity = 1;
					} else if ($current_style['fill-opacity'] > 0) {
						$fopacity = $current_style['fill-opacity'];
					} else if ($current_style['fill-opacity'] < 0) {
						$fopacity = 0;
					}
				}
				$sopacity = 1;
				if (isset($current_style['stroke-opacity'])) {
					if ($current_style['stroke-opacity'] == 0) {
						$sopacity = 0;
					} else if ($current_style['stroke-opacity'] > 1) {
						$sopacity = 1;
					} else if ($current_style['stroke-opacity'] > 0) {
						$sopacity = $current_style['stroke-opacity'];
					} else if ($current_style['stroke-opacity'] < 0) {
						$sopacity = 0;
					}
				}
				$gs = $this->mpdf->AddExtGState(['ca' => $fopacity, 'CA' => $sopacity, 'BM' => '/Normal']);
				$this->mpdf->extgstates[$gs]['fo'] = true;
				$opacitystr = sprintf(' /GS%d gs ', $gs);

				$fillstr = '';
				if (isset($current_style['fill']) && $current_style['fill'] != 'none') {
					$col = $this->colorConverter->convert($current_style['fill'], $this->mpdf->PDFAXwarnings);
					$fillstr = $this->mpdf->SetFColor($col, true);
					$render = "0"; // Fill (only)
					$op = 'f';
				}
				$strokestr = '';
				if ($stroke_width > 0 && $current_style['stroke'] != 'none') {
					$scol = $this->colorConverter->convert($current_style['stroke'], $this->mpdf->PDFAXwarnings);
					if ($scol) {
						$strokestr .= $this->mpdf->SetDColor($scol, true) . ' ';
					}
					$linewidth = $this->ConvertSVGSizePixels($stroke_width);
					if ($linewidth > 0) {
						$strokestr .= sprintf('%.3F w 0 J 0 j ', $linewidth * $this->kp);
						if ($render == -1) {
							$render = "1";
						} // stroke only
						else {
							$render = "2";
						}  // fill and stroke
						$op .= 'S';
					}
				}
				if ($render == -1) {
					return '';
				}
				if ($op == 'fS') {
					$op = 'B';
				}

				$x = $this->txt_data[0]; // mPDF 5.7.4
				$y = $this->txt_data[1]; // mPDF 5.7.4
				$txt = $this->txt_data[2];

				$txt = preg_replace('/\f/', '', $txt);
				$txt = preg_replace('/\r/', '', $txt);
				$txt = preg_replace('/\n/', ' ', $txt);
				$txt = preg_replace('/\t/', ' ', $txt);
				$txt = preg_replace("/[ ]+/u", ' ', $txt);

				if ($this->textjuststarted) {
					$txt = ltrim($txt);
				}  // mPDF 5.7.4
				$this->textjuststarted = false;  // mPDF 5.7.4

				$txt = $this->mpdf->purify_utf8_text($txt);
				if ($this->mpdf->text_input_as_HTML) {
					$txt = $this->mpdf->all_entities_to_utf8($txt);
				}

				$nb = mb_strlen($txt, 'UTF-8');
				$i = 0;
				$sw = 0;
				$subpath_cmd = '';
				while ($i < $nb) {
					//Get next character
					$char = mb_substr($txt, $i, 1, 'UTF-8');


					if (isset($svg_font['glyphs'][$char])) {
						$d = $svg_font['glyphs'][$char]['d'];
						if (isset($svg_font['glyphs'][$char]['horiz-adv-x'])) {
							$horiz_adv_x = $svg_font['glyphs'][$char]['horiz-adv-x'];
						} else {
							$horiz_adv_x = $svg_font['horiz-adv-x'];
						} // missing glyph width
					} else {
						$d = $svg_font['d'];
						$horiz_adv_x = $svg_font['horiz-adv-x']; // missing glyph width
					}
					preg_match_all('/([MZLHVCSQTAmzlhvcsqta])([eE ,\-.\d]+)*/', $d, $commands, PREG_SET_ORDER);
					$subpath_cmd .= sprintf('q %.4F 0 0 %.4F mPDF-AXS(%.4F) %.4F cm ', $scale, -$scale, ($x + $sw * $scale) * $this->kp, -$y * $this->kp);

					$this->subPathInit = true;
					$this->pathBBox = [999999, 999999, -999999, -999999];
					foreach ($commands as $cmd) {
						if (count($cmd) == 3 || (isset($cmd[2]) && $cmd[2] == '')) {
							list($tmp, $command, $arguments) = $cmd;
						} else {
							list($tmp, $command) = $cmd;
							$arguments = '';
						}

						$subpath_cmd .= $this->svgPath($command, $arguments);
					}
					$subpath_cmd .= $op . ' Q ';
					if ($this->pathBBox[2] == -1999998) {
						$this->pathBBox[2] = 100;
					}
					if ($this->pathBBox[3] == -1999998) {
						$this->pathBBox[3] = 100;
					}
					if ($this->pathBBox[0] == 999999) {
						$this->pathBBox[0] = 0;
					}
					if ($this->pathBBox[1] == 999999) {
						$this->pathBBox[1] = 0;
					}


					$sw += $horiz_adv_x;
					$i++;
				}

				$sw *= $scale; // convert stringwidth to units
				// mPDF 5.7.4
				$this->textlength = $sw;
				$this->texttotallength += $this->textlength;

				$path_cmd = sprintf('q %s %s Tr %s %s ', $opacitystr, $render, $fillstr, $strokestr);
				$path_cmd .= $subpath_cmd;
				$path_cmd .= 'Q ';

				unset($this->txt_data[0], $this->txt_data[1], $this->txt_data[2]);
				return $path_cmd;
			}

			// select font
			$style .= ($current_style['font-weight'] == 'bold') ? 'B' : '';
			$style .= ($current_style['font-style'] == 'italic') ? 'I' : '';
			$size = $current_style['font-size'] * $this->kf;

			$current_style['font-family'] = $this->mpdf->SetFont($current_style['font-family'], $style, $size, false);
			$this->mpdf->CurrentFont['fo'] = true;

			$opacitystr = '';
			// mPDF 6
			$fopacity = 1;
			if (isset($current_style['fill-opacity'])) {
				if ($current_style['fill-opacity'] == 0) {
					$fopacity = 0;
				} else if ($current_style['fill-opacity'] > 1) {
					$fopacity = 1;
				} else if ($current_style['fill-opacity'] > 0) {
					$fopacity = $current_style['fill-opacity'];
				} else if ($current_style['fill-opacity'] < 0) {
					$fopacity = 0;
				}
			}
			$sopacity = 1;
			if (isset($current_style['stroke-opacity'])) {
				if ($current_style['stroke-opacity'] == 0) {
					$sopacity = 0;
				} else if ($current_style['stroke-opacity'] > 1) {
					$sopacity = 1;
				} else if ($current_style['stroke-opacity'] > 0) {
					$sopacity = $current_style['stroke-opacity'];
				} else if ($current_style['stroke-opacity'] < 0) {
					$sopacity = 0;
				}
			}
			$gs = $this->mpdf->AddExtGState(['ca' => $fopacity, 'CA' => $sopacity, 'BM' => '/Normal']);
			$this->mpdf->extgstates[$gs]['fo'] = true;
			$opacitystr = sprintf(' /GS%d gs ', $gs);

			$fillstr = '';
			if (isset($current_style['fill']) && $current_style['fill'] != 'none') {
				$col = $this->colorConverter->convert($current_style['fill'], $this->mpdf->PDFAXwarnings);
				$fillstr = $this->mpdf->SetFColor($col, true);
				$render = "0"; // Fill (only)
			}
			$strokestr = '';
			if (isset($current_style['stroke-width']) && $current_style['stroke-width'] > 0 && $current_style['stroke'] != 'none') {
				$scol = $this->colorConverter->convert($current_style['stroke'], $this->mpdf->PDFAXwarnings);
				if ($scol) {
					$strokestr .= $this->mpdf->SetDColor($scol, true) . ' ';
				}
				$linewidth = $this->ConvertSVGSizePixels($current_style['stroke-width']);
				if ($linewidth > 0) {
					$strokestr .= sprintf('%.3F w 1 J 1 j ', $linewidth * $this->kp);
					if ($render == -1) {
						$render = "1";
					} // stroke only
					else {
						$render = "2";
					}  // fill and stroke
				}
			}
			if ($render == -1) {
				return '';
			}

			$x = $this->txt_data[0]; // mPDF 5.7.4
			$y = $this->txt_data[1]; // mPDF 5.7.4
			$txt = $this->txt_data[2];

			$txt = preg_replace('/\f/', '', $txt);
			$txt = preg_replace('/\r/', '', $txt);
			$txt = preg_replace('/\n/', ' ', $txt);
			$txt = preg_replace('/\t/', ' ', $txt);
			$txt = preg_replace("/[ ]+/u", ' ', $txt);

			if ($this->textjuststarted) {
				$txt = ltrim($txt);
			}  // mPDF 5.7.4
			$this->textjuststarted = false;  // mPDF 5.7.4

			$txt = $this->mpdf->purify_utf8_text($txt);
			if ($this->mpdf->text_input_as_HTML) {
				$txt = $this->mpdf->all_entities_to_utf8($txt);
			}

			if ($this->mpdf->usingCoreFont) {
				$txt = mb_convert_encoding($txt, $this->mpdf->mb_enc, 'UTF-8');
			}
			if (preg_match("/([" . $this->mpdf->pregRTLchars . "])/u", $txt)) {
				$this->mpdf->biDirectional = true;
			}

			$textvar = 0;
			$save_OTLtags = $this->mpdf->OTLtags;
			$this->mpdf->OTLtags = [];
			if ($this->mpdf->useKerning) {
				if ($this->mpdf->CurrentFont['haskernGPOS']) {
					if (isset($this->mpdf->OTLtags['Plus'])) {
						$this->mpdf->OTLtags['Plus'] .= ' kern';
					} else {
						$this->mpdf->OTLtags['Plus'] = ' kern';
					}
				} else {
					$textvar = ($textvar | TextVars::FC_KERNING);
				}
			}

			// Use OTL OpenType Table Layout - GSUB & GPOS
			if (isset($this->mpdf->CurrentFont['useOTL']) && $this->mpdf->CurrentFont['useOTL']) {
				$txt = $this->otl->applyOTL($txt, $this->mpdf->CurrentFont['useOTL']);
				$OTLdata = $this->otl->OTLdata;
			}
			$this->mpdf->OTLtags = $save_OTLtags;

			$this->mpdf->magic_reverse_dir($txt, $this->mpdf->directionality, $OTLdata);

			$this->mpdf->CurrentFont['used'] = true;

			$sw = $this->mpdf->GetStringWidth($txt, true, $OTLdata, $textvar); // also adds characters to subset
			// mPDF 5.7.4
			$this->textlength = $sw * 1 / (25.4 / $this->mpdf->dpi);
			$this->texttotallength += $this->textlength;

			$pdfx = $x * $this->kp;
			$pdfy = -$y * $this->kp;

			$aixextra = sprintf(' /F%d %.3F Tf %s %s Tr %s %s ', $this->mpdf->CurrentFont['i'], $this->mpdf->FontSizePt, $opacitystr, $render, $fillstr, $strokestr);

			$path_cmd = 'q 1 0 0 1 mPDF-AXS(0.00) 0 cm '; // Align X-shift
			$path_cmd .= $this->mpdf->Text($pdfx, $pdfy, $txt, $OTLdata, $textvar, $aixextra, 'SVG', true);
			$path_cmd .= " Q\n";

			unset($this->txt_data[0], $this->txt_data[1], $this->txt_data[2]);

			if (isset($current_style['font-size-parent'])) {
				$this->mpdf->SetFontSize($current_style['font-size-parent']);
			}
		} else {
			return ' ';
		}
		// Reset font	// mPDF 5.7.4
		$prev_style = $this->txt_style[count($this->txt_style) - 1];
		$style = '';
		$style .= ($prev_style['font-weight'] == 'bold') ? 'B' : '';
		$style .= ($prev_style['font-style'] == 'italic') ? 'I' : '';
		$size = $prev_style['font-size'] * $this->kf;
		$this->mpdf->SetFont($prev_style['font-family'], $style, $size, false);

		return $path_cmd;
	}
303225
function (data) {
            // already a xml
            if (data.documentElement) {
                return data;
            }
            var xml;
            try {
                // Standard
                if (win['DOMParser']) {
                    xml = new DOMParser().parseFromString(data, 'text/xml');
                } else { // IE
                    xml = new ActiveXObject('Microsoft.XMLDOM');
                    xml.async = false;
                    xml.loadXML(data);
                }
            } catch (e) {
                logger.error('parseXML error :');
                logger.error(e);
                xml = undefined;
            }
            if (!xml || !xml.documentElement || xml.getElementsByTagName('parsererror').length) {
                S.error('Invalid XML: ' + data);
            }
            return xml;
        }
652169
function GetSVG(result) {
  if (!data.svg && !data.svgNode) return;
  var jax = MathJax.Hub.getAllJax()[0]; if (!jax) return;
  var script = jax.SourceElement(),
      svg = script.previousSibling.getElementsByTagName("svg")[0];
  svg.setAttribute("xmlns","http://www.w3.org/2000/svg");

  //
  //  Add the speech text and mark the SVG appropriately
  //
  if (data.speakText){
    for (var i=0, m=svg.childNodes.length; i < m; i++)
      svg.childNodes[i].setAttribute("aria-hidden",true);
    // Note: if aria-label exists, getSpeech preserved it in speakText
    // remove aria-label since labelled-by title is preferred
    svg.removeAttribute("aria-label");
    ID++; var id = "MathJax-SVG-"+ID+"-Title";
    svg.setAttribute("aria-labelledby",id);
    var node = MathJax.HTML.Element("title",{id:id},[result.speakText]);
    svg.insertBefore(node,svg.firstChild);
  }

  if (data.svg){
    //
    //  SVG data is modified to add linebreaks for readability,
    //  and to put back the xlink namespace that is removed in HTML5
    //
    var svgdata = svg.outerHTML.replace(/><([^/])/g,">\n<$1")
                               .replace(/(<\/[a-z]*>)(?=<\/)/g,"$1\n")
                               .replace(/(<(?:use|image) [^>]*)(href=)/g,' $1xlink:$2');

    //
    //  Add the requested data to the results
    //
    result.svg = svgdata;
  }
  if (data.svgNode) result.svgNode = svg;
  result.width = svg.getAttribute("width");
  result.height = svg.getAttribute("height");
  result.style =  svg.style.cssText;
}
870169
function (data) {
        var tag = data.tag;
        var el = '<' + tag + '>';

        if (data.text && data.text.trim()) {
            el += data.text.trim();
        } else {
            data.getchildren().forEach(function (child) {
                el += lib.eltreeToXmlString(child);
            });
        }

        el += '</' + tag + '>';
        return el;
    }
109883
function buildIconsObject(svgFiles, getSvg) {
  return svgFiles
    .map(svgFile => {
      const name = path.basename(svgFile, '.svg');
      const svg = getSvg(svgFile);
      const contents = getSvgContents(svg);
      return { name, contents };
    })
    .reduce((icons, icon) => {
      icons[icon.name] = icon.contents;
      return icons;
    }, {});
}
36709
def svg(self, value):
        

        if len(value) < 500:
            self._svg = value
            return

        try:
            root = ET.fromstring(value)
        except ET.ParseError as e:
            log.error("Can't parse SVG: {}".format(e))
            return
        # SVG is the default namespace no need to prefix it
        ET.register_namespace('xmlns', "http://www.w3.org/2000/svg")
        ET.register_namespace('xmlns:xlink', "http://www.w3.org/1999/xlink")

        if len(root.findall("{http://www.w3.org/2000/svg}image")) == 1:
            href = "{http://www.w3.org/1999/xlink}href"
            elem = root.find("{http://www.w3.org/2000/svg}image")
            if elem.get(href, "").startswith("data:image/"):
                changed = True
                data = elem.get(href, "")
                extension = re.sub(r"[^a-z0-9]", "", data.split(";")[0].split("/")[1].lower())

                data = base64.decodebytes(data.split(",", 1)[1].encode())

                # We compute an hash of the image file to avoid duplication
                filename = hashlib.md5(data).hexdigest() + "." + extension
                elem.set(href, filename)

                file_path = os.path.join(self._project.pictures_directory, filename)
                if not os.path.exists(file_path):
                    with open(file_path, "wb") as f:
                        f.write(data)
                value = filename

        # We dump also large svg on disk to keep .gns3 small
        if len(value) > 1000:
            filename = hashlib.md5(value.encode()).hexdigest() + ".svg"
            file_path = os.path.join(self._project.pictures_directory, filename)
            if not os.path.exists(file_path):
                with open(file_path, "w+", encoding="utf-8") as f:
                    f.write(value)
            self._svg = filename
        else:
            self._svg = value
715012
function(xml) {
    var contexp, fn, formatted, indent, l, lastType, len, lines, ln, pad, reg, transitions, wsexp;
    reg = /(>)(<)(\/*)/g;
    wsexp = /[ ]*(.*)[ ]+\n/g;
    contexp = /(<.+>)(.+\n)/g;
    xml = xml.replace(reg, '$1\n$2$3').replace(wsexp, '$1\n').replace(contexp, '$1\n$2');
    pad = 0;
    formatted = '';
    lines = xml.split('\n');
    indent = 0;
    lastType = 'other';
    transitions = {
      'single->single': 0,
      'single->closing': -1,
      'single->opening': 0,
      'single->other': 0,
      'closing->single': 0,
      'closing->closing': -1,
      'closing->opening': 0,
      'closing->other': 0,
      'opening->single': 1,
      'opening->closing': 0,
      'opening->opening': 1,
      'opening->other': 1,
      'other->single': 0,
      'other->closing': -1,
      'other->opening': 0,
      'other->other': 0
    };
    fn = function(ln) {
      var fromTo, j, key, padding, type, types, value;
      types = {
        single: Boolean(ln.match(/<.+\/>/)),
        closing: Boolean(ln.match(/<\/.+>/)),
        opening: Boolean(ln.match(/<[^!?].*>/))
      };
      type = ((function() {
        var results;
        results = [];
        for (key in types) {
          value = types[key];
          if (value) {
            results.push(key);
          }
        }
        return results;
      })())[0];
      type = type === void 0 ? 'other' : type;
      fromTo = lastType + '->' + type;
      lastType = type;
      padding = '';
      indent += transitions[fromTo];
      padding = ((function() {
        var m, ref1, results;
        results = [];
        for (j = m = 0, ref1 = indent; 0 <= ref1 ? m < ref1 : m > ref1; j = 0 <= ref1 ? ++m : --m) {
          results.push('  ');
        }
        return results;
      })()).join('');
      if (fromTo === 'opening->closing') {
        formatted = formatted.substr(0, formatted.length - 1) + ln + '\n';
      } else {
        formatted += padding + ln + '\n';
      }
    };
    for (l = 0, len = lines.length; l < len; l++) {
      ln = lines[l];
      fn(ln);
    }
    return formatted;
  }
37008
function svgToString (filename) {
  if (!/\.svg$/i.test(filename)) {
    return through()
  }

  return through(function (buf, enc, next) {
    this.push('module.exports=' + JSON.stringify(buf.toString('utf8')))
    next()
  })
}
961908
function mkEl(name) {
  return name === 'svg' ? document.createElementNS(SVG_NS, name) : document.createElement(name)
}
804528
function createNode(nodeName, isSvg) {
    var node = isSvg ? document.createElementNS('http://www.w3.org/2000/svg', nodeName) : document.createElement(nodeName);
    node.normalizedNodeName = nodeName;
    return node;
  }
457642
function (svg) {
		return svg
			.replace(/zIndex="[^"]+"/g, '')
			.replace(/isShadow="[^"]+"/g, '')
			.replace(/symbolName="[^"]+"/g, '')
			.replace(/jQuery[0-9]+="[^"]+"/g, '')
			.replace(/url\([^#]+#/g, 'url(#')
			.replace(/<svg /, '<svg xmlns:xlink="http://www.w3.org/1999/xlink" ')
			.replace(/ (NS[0-9]+\:)?href=/g, ' xlink:href=') // #3567
			.replace(/\n/, ' ')
			// Any HTML added to the container after the SVG (#894)
			.replace(/<\/svg>.*?$/, '</svg>') 
			// Batik doesn't support rgba fills and strokes (#3095)
			.replace(/(fill|stroke)="rgba\(([ 0-9]+,[ 0-9]+,[ 0-9]+),([ 0-9\.]+)\)"/g, '$1="rgb($2)" $1-opacity="$3"')
			/* This fails in IE < 8
			.replace(/([0-9]+)\.([0-9]+)/g, function(s1, s2, s3) { // round off to save weight
				return s2 +'.'+ s3[0];
			})*/

			// Replace HTML entities, issue #347
			.replace(/&nbsp;/g, '\u00A0') // no-break space
			.replace(/&shy;/g,  '\u00AD') // soft hyphen

			// IE specific
			.replace(/<IMG /g, '<image ')
			.replace(/height=([^" ]+)/g, 'height="$1"')
			.replace(/width=([^" ]+)/g, 'width="$1"')
			.replace(/hc-svg-href="([^"]+)">/g, 'xlink:href="$1"/>')
			.replace(/ id=([^" >]+)/g, 'id="$1"') // #4003
			.replace(/class=([^" >]+)/g, 'class="$1"')
			.replace(/ transform /g, ' ')
			.replace(/:(path|rect)/g, '$1')
			.replace(/style="([^"]+)"/g, function (s) {
				return s.toLowerCase();
			});
	}
1262513
function(svg, mine, callback) {
        svg = (new XMLSerializer()).serializeToString(svg);
        svg = 'data:image/svg+xml;charset=utf-8,' + encodeURI(svg);
        if (mine == 'image/svg+xml') {
            callback(null, svg);
            return;
        }
        var img = new Image();
        var canvas = document.createElement('canvas');
        var ctx = canvas.getContext('2d');
        img.onload = function() {
            canvas.width = img.width;
            canvas.height = img.height;
            ctx.drawImage(img, 0, 0);
            var dataURL = canvas.toDataURL(mine);
            callback(null, dataURL);
        };
        img.src = svg;
    }
102459
function normalizeHtml(src, options) {
    var $ = cheerio.load(src, {
        // We should parse html without trying to normalize too much
        xmlMode: false,

        // SVG need some attributes to use uppercases
        lowerCaseAttributeNames: false,
        lowerCaseTags: false
    });
    var toConvert = [];
    var svgContent = {};
    var outputRoot = options.book.options.output;

    imgConversionCache[outputRoot] = imgConversionCache[outputRoot] || {};

    // Find svg images to extract and process
    if (options.convertImages) {
        $('svg').each(function() {
            var content = renderDom($, $(this));
            var svgId = _.uniqueId('svg');
            var dest = svgId+'.svg';

            // Generate filename
            dest = '/'+fs.getUniqueFilename(outputRoot, dest);

            svgContent[dest] = '<?xml version="1.0" encoding="UTF-8"?>'+content;
            $(this).replaceWith($('<img>').attr('src', dest));
        });
    }

    // Generate ID for headings
    $('h1,h2,h3,h4,h5,h6').each(function() {
        if ($(this).attr('id')) return;

        $(this).attr('id', slug($(this).text()));
    });

    // Find images to normalize
    $('img').each(function() {
        var origin;
        var src = $(this).attr('src');

        if (!src) return;
        var isExternal = links.isExternal(src);

        // Transform as relative to the bases
        if (links.isRelative(src)) {
            src = links.toAbsolute(src, options.base, options.output);
        }

        // Convert if needed
        if (options.convertImages) {
            // If image is external and ebook, then downlaod the images
            if (isExternal) {
                origin = src;
                src = '/'+crc.crc32(origin).toString(16)+path.extname(url.parse(origin).pathname);
                src = links.toAbsolute(src, options.base, options.output);
                isExternal = false;
            }

            var ext = path.extname(src);
            var srcAbs = links.join('/', options.base, src);

            // Test image extension
            if (_.contains(imgUtils.INVALID, ext)) {
                if (imgConversionCache[outputRoot][srcAbs]) {
                    // Already converted
                    src = imgConversionCache[outputRoot][srcAbs];
                } else {
                    // Not converted yet
                    var dest = '';

                    // Replace extension
                    dest = links.join(path.dirname(srcAbs), path.basename(srcAbs, ext)+'.png');
                    dest = dest[0] == '/'? dest.slice(1) : dest;

                    // Get a name that doesn't exists
                    dest = fs.getUniqueFilename(outputRoot, dest);

                    options.book.log.debug.ln('detect invalid image (will be converted to png):', srcAbs);

                    // Add to cache
                    imgConversionCache[outputRoot][srcAbs] = '/'+dest;

                    // Push to convert
                    toConvert.push({
                        origin: origin,
                        content: svgContent[srcAbs],
                        source: isExternal? srcAbs : path.join('./', srcAbs),
                        dest: path.join('./', dest)
                    });

                    src = links.join('/', dest);
                }

                // Reset as relative to output
                src = links.toAbsolute(src, options.base, options.output);
            }

            else if (origin) {
                // Need to downlaod image
                toConvert.push({
                    origin: origin,
                    source: path.join('./', srcAbs)
                });
            }
        }

        $(this).attr('src', src);
    });

    // Normalize links
    $('a').each(function() {
        var href = $(this).attr('href');
        if (!href) return;

        if (links.isAnchor(href)) {
            // Keep it as it is
        } else if (links.isRelative(href)) {
            var parts = url.parse(href);
            var absolutePath = links.join(options.base, parts.pathname);
            var anchor = parts.hash || '';


            // If is in navigation relative: transform as content
            if (options.navigation[absolutePath]) {
                absolutePath = options.book.contentLink(absolutePath);
            }

            // If md/adoc/rst files is not in summary
            // or for ebook, signal all files that are outside the summary
            else if (_.contains(parsableExtensions, path.extname(absolutePath)) ||
            _.contains(['epub', 'pdf', 'mobi'], options.book.options.generator)) {
                options.book.log.warn.ln('page', options.input, 'contains an hyperlink to resource outside spine \''+href+'\'');
            }

            // Transform as absolute
            href = links.toAbsolute('/'+absolutePath, options.base, options.output)+anchor;
        } else {
            // External links
            $(this).attr('target', '_blank');
        }

        // Transform extension
        $(this).attr('href', href);
    });

    // Highlight code blocks
    $('code').each(function() {
        // Normalize language
        var lang = _.chain(
                ($(this).attr('class') || '').split(' ')
            )
            .map(function(cl) {
                // Markdown
                if (cl.search('lang-') === 0) return cl.slice('lang-'.length);

                // Asciidoc
                if (cl.search('language-') === 0) return cl.slice('language-'.length);

                return null;
            })
            .compact()
            .first()
            .value();

        var source = $(this).text();
        var blk = options.book.template.applyBlock('code', {
            body: source,
            kwargs: {
                language: lang
            }
        });

        if (blk.html === false) $(this).text(blk.body);
        else $(this).html(blk.body);
    });

    // Replace glossary terms
    var glossary = _.sortBy(options.glossary, function(term) {
        return -term.name.length;
    });

    _.each(glossary, function(term) {
        var r =  new RegExp( '\\b(' + pregQuote(term.name.toLowerCase()) + ')\\b' , 'gi' );
        var includedInFiles = false;

        $('*').each(function() {
            // Ignore codeblocks
            if (_.contains(['code', 'pre', 'a', 'script'], this.name.toLowerCase())) return;

            replaceText($, this, r, function(match) {
                // Add to files index in glossary
                if (!includedInFiles) {
                    includedInFiles = true;
                    term.files = term.files || [];
                    term.files.push(options.navigation[options.input]);
                }
                return '<a href=\''+links.toAbsolute('/GLOSSARY.html', options.base, options.output) + '#' + term.id+'\' class=\'glossary-term\' title=\''+_.escape(term.description)+'\'>'+match+'</a>';
            });
        });
    });

    return {
        html: renderDom($),
        images: toConvert
    };
}