/******************************************************************************* * Copyright 2010 Simon Mieth * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package org.kabeja.svg.generators; import java.util.Iterator; import java.util.Map; import org.kabeja.common.DraftEntity; import org.kabeja.common.LineWidth; import org.kabeja.entities.Hatch; import org.kabeja.entities.util.HatchBoundaryLoop; import org.kabeja.entities.util.HatchLineFamily; import org.kabeja.entities.util.HatchLineIterator; import org.kabeja.entities.util.HatchLineSegment; import org.kabeja.entities.util.HatchPattern; import org.kabeja.math.Bounds; import org.kabeja.math.Point3D; import org.kabeja.math.TransformContext; import org.kabeja.svg.SVGConstants; import org.kabeja.svg.SVGContext; import org.kabeja.svg.SVGPathBoundaryGenerator; import org.kabeja.svg.SVGSAXGeneratorManager; import org.kabeja.svg.SVGUtils; import org.xml.sax.ContentHandler; import org.xml.sax.SAXException; import org.xml.sax.helpers.AttributesImpl; public class SVGHatchGenerator extends AbstractSVGSAXGenerator { public void toSAX(ContentHandler handler, Map svgContext, DraftEntity entity, TransformContext transformContext) throws SAXException { Hatch hatch = (Hatch) entity; SVGSAXGeneratorManager manager = (SVGSAXGeneratorManager) svgContext .get(SVGContext.SVGSAXGENERATOR_MANAGER); Bounds hatchBounds = hatch.getBounds(); if (hatchBounds.isValid()) { AttributesImpl attr = new AttributesImpl(); // the id if (hatch.isSolid()) { super.setCommonAttributes(attr, svgContext, hatch); SVGUtils.addAttribute(attr, SVGConstants.SVG_ATTRIBUTE_FILL, SVGConstants.SVG_ATTRIBUTE_VALUE_CURRENTCOLOR); SVGUtils.startElement(handler, SVGConstants.SVG_GROUP, attr); for (HatchBoundaryLoop loop : hatch.getBoundaryLoops()) { this.loopToSVGPath(handler, loop, manager); } SVGUtils.endElement(handler, SVGConstants.SVG_GROUP); } else { // we will draw a rectangle with the pattern and use then the // boundary path as clip-path attr = new AttributesImpl(); SVGUtils.addAttribute(attr, SVGConstants.XML_ID, SVGUtils .toValidateID(hatch.getID())); boolean clipClipPath = false; if (hatch.getHatchStyle() < 2) { this.islandToClipPath(handler, hatch, manager); clipClipPath = true; SVGUtils.addAttribute(attr, SVGConstants.SVG_ATTRIBUTE_CLIP_PATH, "url(#" + SVGUtils.toValidateID(hatch.getID()) + "-clip)"); } SVGUtils.startElement(handler, SVGConstants.SVG_CLIPPING_PATH, attr); if (clipClipPath) { this.outermostToSVGPath(handler, hatch, manager); } else { for (HatchBoundaryLoop loop : hatch.getBoundaryLoops()) { this.loopToSVGPath(handler, loop, manager); } } SVGUtils.endElement(handler, SVGConstants.SVG_CLIPPING_PATH); HatchPattern pattern = hatch.getDocument().getHatchPattern( hatch.getHatchPatternID()); attr = new AttributesImpl(); SVGUtils.addAttribute(attr, SVGConstants.SVG_ATTRIBUTE_CLIP_PATH, "url(#" + SVGUtils.toValidateID(hatch.getID()) + ")"); SVGUtils.startElement(handler, SVGConstants.SVG_GROUP, attr); SVGUtils.startElement(handler, SVGConstants.SVG_TITLE, new AttributesImpl()); SVGUtils.characters(handler, hatch.getName()); SVGUtils.endElement(handler, SVGConstants.SVG_TITLE); convertHatchPatternToSAX(handler, svgContext, hatchBounds, hatch, transformContext, pattern); SVGUtils.endElement(handler, SVGConstants.SVG_GROUP); } } } protected void islandToClipPath(ContentHandler handler, Hatch hatch, SVGSAXGeneratorManager manager) throws SAXException { AttributesImpl attr = new AttributesImpl(); SVGUtils.addAttribute(attr, SVGConstants.XML_ID, SVGUtils .toValidateID(hatch.getID()) + "-clip"); SVGUtils.startElement(handler, SVGConstants.SVG_CLIPPING_PATH, attr); // we will draw a rectangle with the pattern and use then the // boundary path as clip-path // first the clip-path for (HatchBoundaryLoop loop : hatch.getBoundaryLoops()) { if (!loop.isOutermost()) { loopToSVGPath(handler, loop, manager); } } SVGUtils.endElement(handler, SVGConstants.SVG_CLIPPING_PATH); } protected void outermostToSVGPath(ContentHandler handler, Hatch hatch, SVGSAXGeneratorManager manager) throws SAXException { for (HatchBoundaryLoop loop : hatch.getBoundaryLoops()) { if (loop.isOutermost()) { loopToSVGPath(handler, loop, manager); } } } protected void loopToSVGPath(ContentHandler handler, HatchBoundaryLoop loop, SVGSAXGeneratorManager manager) throws SAXException { StringBuffer buf = new StringBuffer(); Iterator i = loop.getBoundaryEdgesIterator(); if (i.hasNext()) { DraftEntity entity = (DraftEntity) i.next(); buf.append(' '); String d = manager.getSVGPathBoundaryGenerator(entity.getType().getHandle()) .getSVGPath(entity); if (d.length() == 0) { return; } buf.append(d); buf.append(' '); while (i.hasNext()) { entity = (DraftEntity) i.next(); SVGPathBoundaryGenerator part = manager .getSVGPathBoundaryGenerator(entity.getType().getHandle()); buf.append(' '); d = removeStartPoint(part.getSVGPath(entity).trim()); buf.append(d); buf.append(' '); } // every loop as single path if (d.length() > 0) { AttributesImpl attr = new AttributesImpl(); SVGUtils.addAttribute(attr, "d", buf.toString()); SVGUtils.emptyElement(handler, SVGConstants.SVG_PATH, attr); } } } protected String removeStartPoint(String svgPath) { if ((svgPath.length() > 0) && (svgPath.charAt(0) == 'M')) { boolean separator = false; int delemiterCount = 0; for (int i = 1; i < svgPath.length(); i++) { char c = svgPath.charAt(i); if (Character.isWhitespace(c) || (c == ',')) { separator = true; } else { if (separator && (delemiterCount == 2)) { return svgPath.substring(i - 1); } else if (separator) { delemiterCount++; separator = false; } } } } return svgPath; } /* * (non-Javadoc) * * @see org.kabeja.svg.SVGGenerator#toSAX(org.xml.sax.ContentHandler, * java.util.Map) */ public void convertHatchPatternToSAX(ContentHandler handler, Map context, Bounds hatchBounds, Hatch hatch, TransformContext transformContext, HatchPattern p) throws SAXException { // we have to create a tile with all lines double dotLength = (hatchBounds.getWidth() + hatchBounds.getHeight()) / 2 * 0.002; AttributesImpl attr = new AttributesImpl(); Iterator i = p.getLineFamilyIterator(); // patterns.iterator(); while (i.hasNext()) { HatchLineFamily pattern = (HatchLineFamily) i.next(); attr = new AttributesImpl(); if (context.containsKey(SVGContext.LAYER_STROKE_WIDTH)) { LineWidth lw = (LineWidth) context .get(SVGContext.LAYER_STROKE_WIDTH); SVGUtils.addAttribute(attr, SVGConstants.SVG_ATTRIBUTE_STROKE_WITDH, SVGUtils .lineWidthToStrokeWidth(lw)); } SVGUtils.addAttribute(attr, "d", convertPatternToSVGPath( hatchBounds, hatch, pattern, dotLength)); SVGUtils.emptyElement(handler, SVGConstants.SVG_PATH, attr); } } /** * Creates a SVG path of the used line family and fill the complete given * bounds. * * @param b * - The bounds of the DXFHatch * @param pattern * the pattern of the line family * @param dotlength * @return */ protected String convertPatternToSVGPath(Bounds b, Hatch hatch, HatchLineFamily pattern, double dotlength) { StringBuffer buf = new StringBuffer(); Iterator<HatchLineSegment> li = new HatchLineIterator(hatch, pattern); while (li.hasNext()) { HatchLineSegment segment = li.next(); // double angle = Math.toRadians(pattern.getRotationAngle()); Point3D startPoint = segment.getStartPoint(); double x = startPoint.getX(); double y = startPoint.getY(); // the start Point of the line segment buf.append('M'); buf.append(' '); buf.append(SVGUtils.formatNumberAttribute(x)); buf.append(' '); buf.append(SVGUtils.formatNumberAttribute(y)); buf.append(' '); if (segment.isSolid()) { Point3D p = segment.getPointAt(segment.getLength()); buf.append('L'); buf.append(' '); buf.append(SVGUtils.formatNumberAttribute(p.getX())); buf.append(' '); buf.append(SVGUtils.formatNumberAttribute(p.getY())); buf.append(' '); } else { double length = 0; while (segment.hasNext()) { double l = segment.next(); length += Math.abs(l); Point3D p = segment.getPointAt(length); if (l > 0) { buf.append('L'); buf.append(' '); buf.append(SVGUtils.formatNumberAttribute(p.getX())); buf.append(' '); buf.append(SVGUtils.formatNumberAttribute(p.getY())); buf.append(' '); } else if (l < 0) { buf.append('M'); buf.append(' '); buf.append(SVGUtils.formatNumberAttribute(p.getX())); buf.append(' '); buf.append(SVGUtils.formatNumberAttribute(p.getY())); buf.append(' '); } else { // a dot buf.append('l'); buf.append(' '); buf.append(SVGUtils.formatNumberAttribute(dotlength)); buf.append(' '); buf.append(SVGUtils.formatNumberAttribute(dotlength)); buf.append(' '); } } } } return buf.toString(); } }