/**
   * Carries out preprocessing that makes JEuclid handle the document better.
   *
   * @param doc Document
   */
  static void preprocessForJEuclid(Document doc) {
    // underbrace and overbrace
    NodeList list = doc.getElementsByTagName("mo");
    for (int i = 0; i < list.getLength(); i++) {
      Element mo = (Element) list.item(i);
      String parentName = ((Element) mo.getParentNode()).getTagName();
      if (parentName == null) {
        continue;
      }
      if (parentName.equals("munder") && isTextChild(mo, "\ufe38")) {
        mo.setAttribute("stretchy", "true");
        mo.removeChild(mo.getFirstChild());
        mo.appendChild(doc.createTextNode("\u23df"));
      } else if (parentName.equals("mover") && isTextChild(mo, "\ufe37")) {
        mo.setAttribute("stretchy", "true");
        mo.removeChild(mo.getFirstChild());
        mo.appendChild(doc.createTextNode("\u23de"));
      }
    }

    // menclose for long division doesn't allow enough top padding. Oh, and
    // <mpadded> isn't implemented. And there isn't enough padding to left of
    // the bar either. Solve by adding an <mover> with just an <mspace> over#
    // the longdiv, contained within an mrow that adds a <mspace> before it.
    list = doc.getElementsByTagName("menclose");
    for (int i = 0; i < list.getLength(); i++) {
      Element menclose = (Element) list.item(i);
      // Only for longdiv
      if (!"longdiv".equals(menclose.getAttribute("notation"))) {
        continue;
      }
      Element mrow = doc.createElementNS(WebMathsService.NS, "mrow");
      Element mover = doc.createElementNS(WebMathsService.NS, "mover");
      Element mspace = doc.createElementNS(WebMathsService.NS, "mspace");
      Element mspaceW = doc.createElementNS(WebMathsService.NS, "mspace");
      boolean previousElement = false;
      for (Node previous = menclose.getPreviousSibling();
          previous != null;
          previous = previous.getPreviousSibling()) {
        if (previous.getNodeType() == Node.ELEMENT_NODE) {
          previousElement = true;
          break;
        }
      }
      if (previousElement) {
        mspaceW.setAttribute("width", "4px");
      }
      menclose.getParentNode().insertBefore(mrow, menclose);
      menclose.getParentNode().removeChild(menclose);
      mrow.appendChild(mspaceW);
      mrow.appendChild(mover);
      mover.appendChild(menclose);
      mover.appendChild(mspace);
    }
  }
  /**
   * creates a new image
   *
   * @param svgHandle a svg handle
   * @param resourceId the id of the resource from which the image will be created
   */
  protected void createNewImage(SVGHandle svgHandle, String resourceId) {

    if (svgHandle != null && resourceId != null && !resourceId.equals("")) {

      Element resourceElement = null;

      resourceElement =
          svgHandle.getScrollPane().getSVGCanvas().getDocument().getElementById(resourceId);

      final String fresourceId = resourceId;

      if (resourceElement != null) {

        final SVGHandle fhandle = svgHandle;

        // creating the canvas and setting its properties
        final JSVGCanvas canvas =
            new JSVGCanvas() {

              @Override
              public void dispose() {

                removeKeyListener(listener);
                removeMouseMotionListener(listener);
                removeMouseListener(listener);
                disableInteractions = true;
                selectableText = false;
                userAgent = null;

                bridgeContext.dispose();
                super.dispose();
              }
            };

        // the element to be added
        Element elementToAdd = null;

        canvas.setDocumentState(JSVGComponent.ALWAYS_STATIC);
        canvas.setDisableInteractions(true);

        // creating the new document
        final String svgNS = SVGDOMImplementation.SVG_NAMESPACE_URI;
        final SVGDocument doc = (SVGDocument) resourceElement.getOwnerDocument().cloneNode(false);

        // creating the root element
        final Element root =
            (Element)
                doc.importNode(resourceElement.getOwnerDocument().getDocumentElement(), false);
        doc.appendChild(root);

        // removing all the attributes of the root element
        NamedNodeMap attributes = doc.getDocumentElement().getAttributes();

        for (int i = 0; i < attributes.getLength(); i++) {

          if (attributes.item(i) != null) {

            doc.getDocumentElement().removeAttribute(attributes.item(i).getNodeName());
          }
        }

        // adding the new attributes for the root
        root.setAttributeNS(null, "width", imageSize.width + "");
        root.setAttributeNS(null, "height", imageSize.height + "");
        root.setAttributeNS(null, "viewBox", "0 0 " + imageSize.width + " " + imageSize.height);

        // the defs element that will contain the cloned resource node
        final Element defs = (Element) doc.importNode(resourceElement.getParentNode(), true);
        root.appendChild(defs);

        if (resourceElement.getNodeName().equals("linearGradient")
            || resourceElement.getNodeName().equals("radialGradient")
            || resourceElement.getNodeName().equals("pattern")) {

          // the rectangle that will be drawn
          final Element rect = doc.createElementNS(svgNS, "rect");
          rect.setAttributeNS(null, "x", "0");
          rect.setAttributeNS(null, "y", "0");
          rect.setAttributeNS(null, "width", imageSize.width + "");
          rect.setAttributeNS(null, "height", imageSize.height + "");

          elementToAdd = rect;

          // setting that the rectangle uses the resource
          String id = resourceElement.getAttribute("id");

          if (id == null) {

            id = "";
          }

          rect.setAttributeNS(null, "style", "fill:url(#" + id + ");");

          // getting the cloned resource node
          Node cur = null;
          Element clonedResourceElement = null;
          String id2 = "";

          for (cur = defs.getFirstChild(); cur != null; cur = cur.getNextSibling()) {

            if (cur instanceof Element) {

              id2 = ((Element) cur).getAttribute("id");

              if (id2 != null && id.equals(id2)) {

                clonedResourceElement = (Element) cur;
              }
            }
          }

          if (clonedResourceElement != null) {

            // getting the root element of the initial resource
            // element
            Element initialRoot = resourceElement.getOwnerDocument().getDocumentElement();

            // getting the width and height of the initial root
            // element
            double initialWidth = 0, initialHeight = 0;

            try {
              initialWidth =
                  EditorToolkit.getPixelledNumber(initialRoot.getAttributeNS(null, "width"));
              initialHeight =
                  EditorToolkit.getPixelledNumber(initialRoot.getAttributeNS(null, "height"));
            } catch (DOMException ex) {
              ex.printStackTrace();
            }

            if (resourceElement.getNodeName().equals("linearGradient")) {

              if (resourceElement.getAttributeNS(null, "gradientUnits").equals("userSpaceOnUse")) {

                double x1 = 0, y1 = 0, x2 = 0, y2 = 0;

                // normalizing the values for the vector to fit
                // the rectangle
                try {
                  x1 = Double.parseDouble(resourceElement.getAttributeNS(null, "x1"));
                  y1 = Double.parseDouble(resourceElement.getAttributeNS(null, "y1"));
                  x2 = Double.parseDouble(resourceElement.getAttributeNS(null, "x2"));
                  y2 = Double.parseDouble(resourceElement.getAttributeNS(null, "y2"));

                  x1 = x1 / initialWidth * imageSize.width;
                  y1 = y1 / initialHeight * imageSize.height;
                  x2 = x2 / initialWidth * imageSize.width;
                  y2 = y2 / initialHeight * imageSize.height;
                } catch (NumberFormatException | DOMException ex) {
                  ex.printStackTrace();
                }

                clonedResourceElement.setAttributeNS(null, "x1", format.format(x1));
                clonedResourceElement.setAttributeNS(null, "y1", format.format(y1));
                clonedResourceElement.setAttributeNS(null, "x2", format.format(x2));
                clonedResourceElement.setAttributeNS(null, "y2", format.format(y2));
              }

            } else if (resourceElement.getNodeName().equals("radialGradient")) {

              if (resourceElement.getAttributeNS(null, "gradientUnits").equals("userSpaceOnUse")) {

                double cx = 0, cy = 0, r = 0, fx = 0, fy = 0;

                // normalizing the values for the circle to fit
                // the rectangle
                try {
                  cx = Double.parseDouble(resourceElement.getAttributeNS(null, "cx"));
                  cy = Double.parseDouble(resourceElement.getAttributeNS(null, "cy"));
                  r = Double.parseDouble(resourceElement.getAttributeNS(null, "r"));
                  fx = Double.parseDouble(resourceElement.getAttributeNS(null, "fx"));
                  fy = Double.parseDouble(resourceElement.getAttributeNS(null, "fy"));

                  cx = cx / initialWidth * imageSize.width;
                  cy = cy / initialHeight * imageSize.height;

                  r =
                      r
                          / (Math.abs(
                              Math.sqrt(Math.pow(initialWidth, 2) + Math.pow(initialHeight, 2))))
                          * Math.abs(
                              Math.sqrt(
                                  Math.pow(imageSize.width, 2) + Math.pow(imageSize.width, 2)));

                  fx = fx / initialWidth * imageSize.width;
                  fy = fy / initialHeight * imageSize.height;
                } catch (NumberFormatException | DOMException ex) {
                  ex.printStackTrace();
                }

                clonedResourceElement.setAttributeNS(null, "cx", format.format(cx));
                clonedResourceElement.setAttributeNS(null, "cy", format.format(cy));
                clonedResourceElement.setAttributeNS(null, "r", format.format(r));
                clonedResourceElement.setAttributeNS(null, "fx", format.format(fx));
                clonedResourceElement.setAttributeNS(null, "fy", format.format(fy));
              }

            } else if (resourceElement.getNodeName().equals("pattern")) {

              if (resourceElement.getAttributeNS(null, "patternUnits").equals("userSpaceOnUse")) {

                double x = 0, y = 0, w = 0, h = 0;

                // normalizing the values for the vector to fit
                // the rectangle
                try {
                  String xString = resourceElement.getAttributeNS(null, "x");
                  if (!xString.equals("")) {
                    x = Double.parseDouble(xString);
                  }
                  String yString = resourceElement.getAttributeNS(null, "y");
                  if (!yString.equals("")) {
                    y = Double.parseDouble(yString);
                  }
                  String wString = resourceElement.getAttributeNS(null, "w");
                  if (!wString.equals("")) {
                    w = Double.parseDouble(wString);
                  }
                  String hString = resourceElement.getAttributeNS(null, "h");
                  if (!hString.equals("")) {
                    h = Double.parseDouble(hString);
                  }

                  x = x / initialWidth * imageSize.width;
                  y = y / initialHeight * imageSize.height;
                  w = w / initialWidth * imageSize.width;
                  h = h / initialHeight * imageSize.height;
                } catch (NumberFormatException | DOMException ex) {
                  ex.printStackTrace();
                }

                clonedResourceElement.setAttributeNS(null, "x", format.format(x));
                clonedResourceElement.setAttributeNS(null, "y", format.format(y));
                clonedResourceElement.setAttributeNS(null, "width", format.format(w));
                clonedResourceElement.setAttributeNS(null, "height", format.format(h));
              }
            }
          }

        } else if (resourceElement.getNodeName().equals("marker")) {

          // the line that will be drawn
          final Element line = doc.createElementNS(svgNS, "line");
          line.setAttributeNS(null, "x1", (((double) imageSize.width) / 2) + "");
          line.setAttributeNS(null, "y1", (((double) imageSize.height) / 2) + "");
          line.setAttributeNS(null, "x2", ((double) imageSize.width / 2) + "");
          line.setAttributeNS(null, "y2", imageSize.height + "");

          elementToAdd = line;

          // setting that the line uses the resource
          String id = resourceElement.getAttribute("id");
          if (id == null) id = "";
          line.setAttributeNS(
              null, "style", "stroke:none;fill:none;marker-start:url(#" + id + ");");
        }

        root.appendChild(elementToAdd);

        // adding a rendering listener to the canvas
        GVTTreeRendererAdapter gVTTreeRendererAdapter =
            new GVTTreeRendererAdapter() {

              @Override
              public void gvtRenderingCompleted(GVTTreeRendererEvent evt) {

                Image bufferedImage = canvas.getOffScreen();

                if (bufferedImage != null) {

                  Graphics g = bufferedImage.getGraphics();
                  Color borderColor = MetalLookAndFeel.getSeparatorForeground();

                  g.setColor(borderColor);
                  g.drawRect(0, 0, imageSize.width - 1, imageSize.height - 1);
                }

                setImage(fhandle, fresourceId, bufferedImage);

                // refreshing the panels that have been created when no
                // image was available for them
                Image image = null;

                for (ResourceRepresentation resourceRepresentation :
                    new LinkedList<ResourceRepresentation>(resourceRepresentationList)) {

                  if (resourceRepresentation != null) {

                    resourceRepresentation.refreshRepresentation();
                    image = resourceRepresentation.getImage();

                    if (image != null) {

                      resourceRepresentationList.remove(resourceRepresentation);
                    }
                  }
                }

                canvas.removeGVTTreeRendererListener(this);
                canvas.stopProcessing();
                canvas.dispose();
              }
            };

        canvas.addGVTTreeRendererListener(gVTTreeRendererAdapter);

        // setting the document for the canvas
        canvas.setSVGDocument(doc);

        canvas.setBackground(Color.white);
        canvas.setBounds(1, 1, imageSize.width, imageSize.height);
      }
    }
  }