@Override public void produceVisibleOutput(QContent qc, boolean bInit, boolean bPlain) throws OmException { // Get actual current value of string String sCurrent = getQuestion().applyPlaceholders(sEquation); if (bPlain) { // Put text equivalent Element eDiv = qc.createElement("div"); // Can't use span because they aren't allowed to contain things eDiv.setAttribute("style", "display:inline"); qc.addInlineXHTML(eDiv); XML.createText(eDiv, (bSpaceBefore ? " " : "") + getString("alt") + (bSpaceAfter ? " " : "")); qc.addTextEquivalent(getString("alt")); // Put each placeholder for (int i = 0; i < apPlaces.length; i++) { // Check content is not hidden Place p = apPlaces[i]; if ((!p.bImplicit && !p.qc.isDisplayed()) || (p.bImplicit && !p.qc.isChildDisplayed())) continue; // Label followed by content of placeholder Element ePlace = XML.createChild(eDiv, "div"); if (p.sLabel != null) { Element eLabel = XML.createChild(ePlace, p.sLabelFor == null ? "span" : "label"); XML.createText(eLabel, p.sLabel + " "); qc.addTextEquivalent(p.sLabel); if (p.sLabelFor != null) eLabel.setAttribute( "for", LabelComponent.getLabel(getQDocument(), bPlain, p.sLabelFor)); } qc.setParent(ePlace); p.qc.produceOutput(qc, bInit, bPlain); qc.unsetParent(); } } else { // Check background colour, foreground, and zoom Color cBackground = getBackground(); if (cBackground == null) cBackground = Color.white; Color cForeground = getQuestion().isFixedColour() ? convertRGB(getQuestion().getFixedColourFG()) : Color.black; double dZoom = getQuestion().getZoom(); // Hash to filename/identifier String sFilename = "eq" + (sCurrent.hashCode() + cBackground.hashCode() * 3 + cForeground.hashCode() * 7 + (new Double(dZoom)).hashCode() * 11) + (getBoolean(PROPERTY_TEXTFONT) ? "t" : "e") + ".png"; // Make actual image if needed, also get placeholder positions if (!sFilename.equals(sSent)) { e = Equation.create(sCurrent, (float) dZoom); if (getBoolean(PROPERTY_TEXTFONT)) e.setFont("Verdana", new int[] {13, 11, 9}); BufferedImage bi = e.render(cForeground, cBackground, true); qc.addResource(sFilename, "image/png", QContent.convertPNG(bi)); for (int i = 0; i < apPlaces.length; i++) { Point p = e.getPlaceholder(apPlaces[i].sID); apPlaces[i].iActualX = p.x; apPlaces[i].iActualY = p.y; } sSent = sFilename; } Element eEnsureSpaces = qc.createElement("div"); eEnsureSpaces.setAttribute("class", "equation"); qc.addInlineXHTML(eEnsureSpaces); // If there's a space before, add one here too (otherwise IE eats it) if (bSpaceBefore) XML.createText(eEnsureSpaces, " "); String sImageID = QDocument.ID_PREFIX + getID() + "_img"; Element eImg = XML.createChild(eEnsureSpaces, "img"); eImg.setAttribute("id", sImageID); eImg.setAttribute("onmousedown", "return false;"); // Prevent Firefox drag/drop eImg.setAttribute("src", "%%RESOURCES%%/" + sFilename); eImg.setAttribute("alt", getString("alt")); eImg.setAttribute("style", "vertical-align:-" + (e.getHeight() - e.getBaseline()) + "px;"); if (bSpaceAfter) XML.createText(eEnsureSpaces, " "); qc.addTextEquivalent(getString("alt")); String sJavascript = "addOnLoad(function() { inlinePositionFix('" + sImageID + "'"; for (int i = 0; i < apPlaces.length; i++) { Place p = apPlaces[i]; // Must get the label even though not using it, just to indicate that // it's been used if (p.sLabelFor != null) LabelComponent.getLabel(getQDocument(), bPlain, p.sLabelFor); int iEffectiveWidth = (int) Math.round(dZoom * p.iWidth), iEffectiveHeight = (int) Math.round(dZoom * p.iHeight); String sPlaceholderID = QDocument.ID_PREFIX + getID() + "_" + p.sID; Element ePlace = XML.createChild(eEnsureSpaces, "div"); ePlace.setAttribute("class", "placeholder"); ePlace.setAttribute("id", sPlaceholderID); ePlace.setAttribute( "style", "width:" + iEffectiveWidth + "px; " + "height:" + iEffectiveHeight + "px; " + "visibility:hidden;"); QComponent qcPlaceComponent = p.qc; if (qcPlaceComponent.isPropertyDefined(PROPERTY_FORCEWIDTH) && qcPlaceComponent.isPropertyDefined(PROPERTY_FORCEHEIGHT)) { qcPlaceComponent.setInteger(PROPERTY_FORCEWIDTH, iEffectiveWidth); qcPlaceComponent.setInteger(PROPERTY_FORCEHEIGHT, iEffectiveHeight); } if (p.sLabel != null) qc.addTextEquivalent(p.sLabel); qc.setParent(ePlace); qcPlaceComponent.produceOutput(qc, bInit, bPlain); qc.unsetParent(); sJavascript += ",['" + sPlaceholderID + "'," + p.iActualX + "," + p.iActualY + "]"; } sJavascript += "); });"; if (apPlaces.length > 0) // No JS needed if there weren't any placeholders { Element eScript = XML.createChild(eEnsureSpaces, "script"); eScript.setAttribute("type", "text/javascript"); XML.createText(eScript, sJavascript); } } }
@Override protected void initChildren(Element eThis) throws OmException { Node nPrevious = eThis.getPreviousSibling(); if (nPrevious != null && nPrevious instanceof Text) { String sText = ((Text) nPrevious).getData(); if (sText.length() > 0 && Character.isWhitespace(sText.charAt(sText.length() - 1))) bSpaceBefore = true; } Node nAfter = eThis.getNextSibling(); if (nAfter != null && nAfter instanceof Text) { String sText = ((Text) nAfter).getData(); if (sText.length() > 0 && Character.isWhitespace(sText.charAt(0))) bSpaceAfter = true; } List<Place> lPlaces = new LinkedList<Place>(); int iPlace = 0; StringBuffer sbText = new StringBuffer(); for (Node n = eThis.getFirstChild(); n != null; n = n.getNextSibling()) { if (n instanceof Element) { Element eplace = (Element) n; if (!eplace.getTagName().equals("eplace")) throw new OmFormatException("<equation> may only contain text and <eplace> tags"); Element[] aeChildren = XML.getChildren(eplace); QComponent qcChild; boolean bImplicit = false; if (aeChildren.length != 1) // Treats more than one child as inside <t> { qcChild = getQDocument().build(this, eplace, "t"); bImplicit = true; } else // Treats single child as specific component (auto-sizing works) qcChild = getQDocument().build(this, aeChildren[0], null); addChild(qcChild); // Must be stored in standard child array so it // can be found etc. // See if width/height is specified int iWidth, iHeight; if (eplace.hasAttribute("width") && eplace.hasAttribute("height")) { try { iWidth = Integer.parseInt(eplace.getAttribute("width")); iHeight = Integer.parseInt(eplace.getAttribute("height")); } catch (NumberFormatException nfe) { throw new OmFormatException("<equation> <eplace>: width= and height= must be integers"); } } else { Dimension d = qcChild.getApproximatePixelSize(); if (d == null) throw new OmFormatException( "<equation> <eplace>: Except for components that support automatic " + "size estimation and fixing, <eplace> must include width= and height="); iWidth = d.width; iHeight = d.height; } Place p = new Place(); p.sID = "p" + (iPlace++); p.qc = qcChild; p.iWidth = iWidth; p.iHeight = iHeight; p.bImplicit = bImplicit; if (!eplace.hasAttribute("label")) throw new OmFormatException("<equation> <eplace>: Must include label="); if (eplace.hasAttribute("label")) p.sLabel = eplace.getAttribute("label"); else p.sLabel = null; if (eplace.hasAttribute("for")) p.sLabelFor = eplace.getAttribute("for"); else if (qcChild instanceof Labelable) p.sLabelFor = qcChild.getID(); lPlaces.add(p); // Add in the equation format text representing the placeholder sbText.append("\\placeholder{" + p.sID + "}{" + p.iWidth + "," + p.iHeight + "}"); } else if (n instanceof Text) { sbText.append(n.getNodeValue()); } } sEquation = sbText.toString(); apPlaces = lPlaces.toArray(new Place[0]); }