All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.adobe.fontengine.font.cff.NonOverlappingOutlineConsumer Maven / Gradle / Ivy

There is a newer version: 2024.11.18751.20241128T090041Z-241100
Show newest version
package com.adobe.fontengine.font.cff;

import java.io.Serializable;

import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.Collections;
import java.util.Comparator;

import com.adobe.fontengine.font.Matrix;
import com.adobe.fontengine.font.OutlineConsumer;
import com.adobe.fontengine.math.Math2;

/**
 * A class that consumes an outline and generates an outline with overlapping paths removed
 */
public class NonOverlappingOutlineConsumer implements OutlineConsumer {

	private static final int MAX_NUM_SUBPATHS=1000;
	private static final int MAX_NUM_SEGMENTS=1000;
	
	//Segment flags
	private static final int SEG_DELETE	= (1<<0);	//Segment deleted (must be bit zero) 
	private static final int SEG_ISECT	= (1<<1);	// Segment intersected 
	private static final int SEG_WIND_TEST =(1<<2);	// Segment winding tested 
	
 	// Scale to ten-thousandths of an em 
	private static float milliem(float unitsPerEm, float v)	{
		return (float) (unitsPerEm*(v)/1000.0);
	}	
	
	//	Exception specific to removal of overlapping paths
	private static class ISectException extends Exception {

		static final long serialVersionUID = 1;

		public ISectException () {
			super ();
		}

		public ISectException (String message) {
			super (message);
		}

		public ISectException (String message, Throwable cause) {
			super (message, cause);
		}

		public ISectException (Throwable cause) {
			super (cause);
		}
	}

	//	private class representing a point
    private static class Point 
    {
    	float x, y;
    	
    	Point(float x, float y) {
    		this.x = x;
    		this.y = y;
    	}
    	
    	Point() {}
    	
    	Point(Point p) {
    		this.x = p.x;
    		this.y = p.y;
    	}
    	
    	public String toString() {
    		return "(" + x + "," + y + ")";
    	}
    	
    	public boolean equals(Point p) {
    		return (Math2.epsilonEquals(this.x, p.x) && Math2.epsilonEquals(this.y, p.y));
    	}
    }
    
    //  private class representing a rectangle
    private static class Rect {
    	float left, bottom, right, top;
    	
    	Rect(float l, float b, float r, float t) {
    		left = l;
    		bottom = b;
    		right = r;
    		top = t;
    	}
    	
    	private void grow(Rect r1) {
    		if (this.left > r1.left)
    			this.left = r1.left;
    		if (this.bottom	 > r1.bottom)
    			this.bottom = r1.bottom;
    		if (this.right < r1.right)
    			this.right = r1.right;
    		if (this.top < r1.top)
    			this.top = r1.top;
    	}
    	
    	private boolean overlap(Rect r1) {
    		boolean doesNotOverlap = 
    			((this.right < r1.left) || (this.left > r1.right) || 
    					(this.top < r1.bottom) || (this.bottom > r1.top));
    		return !doesNotOverlap;
    	}
    	
    	private Rect iSect(Rect r1) {
    		float l, r, t, b;
    		l = Math.max(this.left, r1.left);
    		r = Math.min(right, r1.right);
    		t = Math.min(top, r1.top);
    		b = Math.max(bottom, r1.bottom);
    		return new Rect(l, b, r, t);
    	}
    	
    	public String toString() {
    		return "left:" + left + " right:" + right + "bottom: " + bottom + "top: " + top;
    	}
    }
    
    //  private helper class representing an interception and winding in order to compute
    // if a segment is inside of outside of path
  	private static class ICept {
		float ic;
		int winding;
		
		ICept(float ic, int winding) {
			this.ic = ic;
			this.winding = winding;
		}
		
	}
  	
  	//  private class representing a line segment
    private static class Line extends Segment
    {
        Point p0;
        Point p1;
        float t0, t1; //start and end of split
        Rect bounds;
        int id;
        
        Line(float x0, float y0, float x1, float y1)
        {
           p0 = new Point(x0, y0);
           p1 = new Point(x1, y1);
           t0 = 0;
           t1 = 1;
           setBounds();
        }
        
        Line(Point start, Point end)
        {
            p0 = new Point(start);
            p1 = new Point(end);
            t0 = 0;
            t1 = 1;
            setBounds();
         }
        
    	public String toString() {
    		String flagStr = ((this.flags & SEG_DELETE) != 0) ? "Delete," : "";
    		flagStr = ((this.flags & SEG_WIND_TEST) != 0) ? flagStr+" WindTested" : flagStr;
    		String s = "Point p0:" + p0.toString() + " Point p1:" + p1.toString() + "t0:" + t0 + " t1:" + t1 + "flag:" + flagStr +" bounds:" + bounds.toString();
    		return s;
    	}
    	
        int getID() {
        	return id;
        }
        
     	Point getFirstPoint() {
       		return p0;
       	}
      	
     	Point getLastPoint() {
       		return p1;
       	}
         
     	boolean horizontal() {
     		return (Math.abs(p1.x - p0.x) > 
     		Math.abs(p1.y - p0.y));
     	}
     	
        //compute bounds for the line segment
    	void setBounds() {
           	float left, bottom, right,  top;
           	if (p0.x < p1.x) {
        		left = p0.x;
        		right = p1.x;
        	}
        	else 
        	{
        		right = p0.x;
        		left = p1.x;       		
        	}
        	if (p0.y < p1.y) {
        		bottom = p0.y;
        		top = p1.y;
        	}
        	else 
        	{
        		top = p0.y;
        		bottom = p1.y;       		
        	}
           	bounds = new Rect(left, bottom, right,  top);     		
     	}
    	
    	void roundControlPoints(float unitsPerEm) {
    		p0.x = Math.round(p0.x);
    		p0.y = Math.round(p0.y);
    		p1.x = Math.round(p1.x);
    		p1.y = Math.round(p1.y);  		
    	}
    	
        Rect getBounds() {
         	
        	if (bounds != null)
        		return bounds;
        	
        	setBounds();
        	return bounds;
        }
        
    	boolean equalControlPoints(Segment s) {
    		if (s instanceof Line) {
    			Line l = (Line) s;
    		
    			return (Math2.epsilonEquals(p0.x, l.p0.x) && Math2.epsilonEquals(p0.y, l.p0.y) &&
     				Math2.epsilonEquals(p1.x, l.p1.x) && Math2.epsilonEquals(p1.y, l.p1.y));
    		}
    		return false;
     	}
     	
     	boolean reverseControlPoints(Segment s) {
       		if (s instanceof Line) {
    			Line l = (Line) s;
   		
    			return (Math2.epsilonEquals(p0.x, l.p1.x) && Math2.epsilonEquals(p0.y, l.p1.y) &&
     				Math2.epsilonEquals(p1.x, l.p0.x) && Math2.epsilonEquals(p1.y, l.p0.y));
       		}
       		return false;
     	}
     	
       Segment copySegment() {
    	   
    	   Line newl = new Line(p0, p1);
    	   newl.t0 = t0;
    	   newl.t1 = t1;
    	   newl.id = id;
    	   newl.flags = flags;
    	   newl.origSeg = origSeg;
     	   return newl;
       }
    	
       // compute intersection point
       private Point computeISectPoint(float t) {
        	float x = 	p0.x + t*(p1.x - p0.x);
        	float y = p0.y + t*(p1.y - p0.y);
        	return new Point(x, y);        	
        }
        
        // Check if line u0->u1 intersects with rectangle r. Return true if intersection
        // else false. Cohen-Sutherland algorithm. 
        private boolean iSectRect(Rect r) {
        	final int CLIP_CODE_LEFT = 1;
        	final int CLIP_CODE_RIGHT = 2;
        	final int CLIP_CODE_BOTTOM = 4;
            final int CLIP_CODE_TOP = 8;

        	int c0;
        	int c1;
    	
        	if (p0.x < r.left)
        		c0 = CLIP_CODE_LEFT;
        	else if (p0.x > r.right)
        		c0 = CLIP_CODE_RIGHT;
        	else
        		c0 = 0;

        	if (p0.y > r.top)
        		c0 |= CLIP_CODE_TOP;
        	else if (p0.y < r.bottom)
        		c0 |= CLIP_CODE_BOTTOM;
    	
        	if (c0 == 0)
        		return true;

        	if (p1.x < r.left)
        		c1 = CLIP_CODE_LEFT;
        	else if (p1.x > r.right)
        		c1 = CLIP_CODE_RIGHT;
        	else
        		c1 = 0;

        	if (p1.y > r.top)
        		c1 |= CLIP_CODE_TOP;
        	else if (p1.y < r.bottom)
        		c1 |= CLIP_CODE_BOTTOM;

        	if (c1 == 0)
        		return true;

        	return (c0 & c1) == 0;
        }
        
        // Intersect lines. Returns as a list of 2 t values per line
        private List iSectLine(Line l) {
        	float a0 = p1.y - p0.y;
        	float b0 = p1.x - p0.x;
        	float a1 = l.p1.y - l.p0.y;
        	float b1 = l.p1.x - l.p0.x;
        	float d = a1*b0 - b1*a0;
        	float j = p0.y - l.p0.y;
        	float k = p0.x - l.p0.x;
        	float n0 = b1*j - a1*k;
        	List iSects = new ArrayList();

        	if (d == 0)
        		{
        		// Lines are parallel 
        		if (n0 == 0)
        			{
        			// Lines are coincident; compute intersection rect 
        			Rect lBounds = l.getBounds();

        			if (!bounds.overlap(lBounds))
        				return iSects;

        			Rect r = bounds.iSect(lBounds);

        			if (r.left == r.right && r.bottom == r.top)
        				{
        				float[] t = new float[2];
        				// Lines touch at end points 
        				if (b0 != 0)
        					{
        					// Compute t horizontally 
						t[0] = (Math2.epsilonEquals(p1.x, r.left)) ? 1 : 0;
        					t[1] = (Math2.epsilonEquals(l.p1.x, r.left)) ? 1 : 0;
        					}
        				else
        					{
        					// Compute t vertically 
						t[0] = (Math2.epsilonEquals(p1.y, r.bottom)) ?  1 : 0;
						t[1] = (Math2.epsilonEquals(l.p1.y, r.bottom)) ? 1 : 0;
        					}
        				iSects.add(t);
        				return iSects;
        				}
        			else if (r.right - r.left > r.top - r.bottom)
        				{
        				// Compute t horizontally 
           				float[] t = new float[2];
           				float[] t1 = new float[2];
        				t[0] = (r.left - p0.x)/b0;
        				t[1] = (r.left - l.p0.x)/b1;
        				t1[0] = (r.right - p0.x)/b0;
        				t1[1] = (r.right - l.p0.x)/b1;
        				iSects.add(t);
        				iSects.add(t1);
        				return iSects;
        				}
        			else
        				{
        				// Compute t vertically 
          				float[] t = new float[2];
           				float[] t1 = new float[2];
        				t[0] = (r.bottom - p0.y)/a0;
        				t[1] = (r.bottom - l.p0.y)/a1;
         				t1[0] = (r.top - p0.y)/a0;
        				t1[1] = (r.top - l.p0.y)/a1;
           				iSects.add(t);
        				iSects.add(t1);
        				return iSects;
        				}
        			}
        		else
        			return iSects;
        		}

        	// Non-parallel lines; check they both have intercepts 
			float[] t = new float[2];
        	t[0] = n0/d;
        	if (t[0] < 0 || t[0] > 1)
        		return iSects;

        	t[1] = (b0*j - a0*k)/d;
        	if (t[1] < 0 || t[1] > 1)
        		return iSects;

        	iSects.add(t);
        	return iSects;	
        }
        
        List solveAtX(float x) {
        	float value = p0.y + (x-p0.x)*(p1.y - p0.y)/(p1.x - p0.x);
           	ICept icept = new ICept(value, getWindingX());
        	List icList = new ArrayList();
        	icList.add(icept);
        	return icList;        
        }
        
       List solveAtY(float y) {
        	float value = p0.x + (y-p0.y)*(p1.x - p0.x)/(p1.y - p0.y);
        	ICept icept = new ICept(value, getWindingY());
        	List icList = new ArrayList();
        	icList.add(icept);
        	return icList;
        }

        int getWindingX() {
        	return getWinding(p0.x, p1.x);
        }
       
        int getWindingY() {
        	return getWinding(p0.y, p1.y);
        }
    }
  	
    //  private class representing a curve segment
    private static class Curve extends Segment
    {
    	Point p0, p1, p2, p3;
     	float t0, t1; //split start and end
    	long depth; //recursion depth
    	Rect bounds;
    	int id;
 
       	private static class Limit {
    		float min, max;
    		
    		Limit(float minimum, float maximum) {
    			min = minimum;
    			max = maximum;
    		}
    		
    	}
  
    	Curve(float x0, float y0,
    		  float x1, float y1, 
              float x2, float y2, 
              float x3, float y3)
        {   		
            p0 = new Point(x0, y0);
            p1 = new Point(x1, y1);
            p2 = new Point(x2, y2);
            p3 = new Point(x3, y3);
            t0 = 0;
            t1 = 1;
            depth = 0;
            setBounds();
        }
    	
    	Curve(Point pnt0, Point pnt1, Point pnt2, Point pnt3)
          {      		
              p0 = new Point(pnt0);
              p1 = new Point(pnt1);
              p2 = new Point(pnt2);
              p3 = new Point(pnt3);
              t0 = 0;
              t1 = 1;
              depth = 0;
              setBounds();
         }
    	
    	public String toString() {
    		String flagStr = ((this.flags & SEG_DELETE) != 0) ? "Delete," : "";
    		flagStr = ((this.flags & SEG_WIND_TEST) != 0) ? flagStr+" WindTested" : flagStr;
    		String s = "Point p0:" + p0.toString() + " Point p1:" + p1.toString() + "Point p2:" + p2.toString() + " Point p3:" + p3.toString() + "t0:" + t0 + " t1:" + t1 + "flag:" + flagStr+" bounds:" + bounds.toString();
    		return s;
    	}

    	
        int getID() {
        	return id;
        }
        
    	private Limit getBezLimit(float p0, float p1, float p2, float p3, float min, float max) 
    	{	
    		float t[] = new float[2];
    		int i = 0;
    		float a = p3 - 3*(p2 - p1) - p0;
    		float b = p2 - 2*p1 + p0;
    		float c = p1 - p0;
    		float minimum = min;
    		float maximum = max;
    		if (a == 0)
    		{
    			if (b != 0)
    				t[i++] = -c/(2*b);
    		}
    		else
    		{
    			float r = b*b - a*c;
    			if (r >= 0)
    			{
    				r = (float)Math.sqrt(r);
    				t[i++] = (-b + r)/a;
    				t[i++] = (-b - r)/a;
    			}
    		}
    		
    		for (i--; i >=0 ; i--) {
     			if (t[i] > 0 && t[i] < 1)
    			{
    				float limit = t[i]*(t[i]*(t[i]*a + 3*b) + 3*c) + p0;
    				if (limit < min)
    					minimum = limit;
    				else if (limit > max)
    					maximum = limit;
    			}
    		}
    		Limit l = new Limit(minimum, maximum);
    		return l;
    	}
    	
     	Point getFirstPoint() {
       		return p0;
       	}
      	
     	Point getLastPoint() {
       		return p3;
       	}
         
     	boolean horizontal() {
     		return (Math.abs(p3.x - p0.x) > 
     		Math.abs(p3.y - p0.y));
     	}
     	
     	boolean equalControlPoints(Segment s) {
    		if (s instanceof Curve) {
    			Curve c = (Curve) s;
    		
    			return (Math2.epsilonEquals(p0.x, c.p0.x) && Math2.epsilonEquals(p0.y, c.p0.y) &&
     				Math2.epsilonEquals(p1.x, c.p1.x) && Math2.epsilonEquals(p1.y, c.p1.y) &&
     				Math2.epsilonEquals(p2.x, c.p2.x) && Math2.epsilonEquals(p2.y, c.p2.y) &&
     				Math2.epsilonEquals(p3.x, c.p3.x) && Math2.epsilonEquals(p3.y, c.p3.y));
    		}
    		return false;
    		
     	}
     	
     	boolean reverseControlPoints(Segment s) {
    		if (s instanceof Curve) {
    			Curve c = (Curve) s;
    		
    			return (Math2.epsilonEquals(p0.x, c.p3.x) && Math2.epsilonEquals(p0.y, c.p3.y) &&
     				Math2.epsilonEquals(p1.x, c.p2.x) && Math2.epsilonEquals(p1.y, c.p2.y) &&
     				Math2.epsilonEquals(p2.x, c.p1.x) && Math2.epsilonEquals(p2.y, c.p1.y) &&
     				Math2.epsilonEquals(p3.x, c.p0.x) && Math2.epsilonEquals(p3.y, c.p0.y));
    		}
    		
    		return false;
     	}
     	
        Segment copySegment() {

        	Curve newc = new Curve(p0, p1, p2, p3);
        	newc.t0 = t0;
        	newc.t1 = t1;
        	newc.id = id;
        	newc.flags = flags;
        	newc.origSeg = origSeg;
        	return newc;
        }
        
      void roundControlPoints(float unitsPerEm) {
     		
     		p0.x = Math.round(p0.x);
     		p0.y = Math.round(p0.y);
     		p1.x = Math.round(p1.x);
     		p1.y = Math.round(p1.y);
     		p2.x = Math.round(p2.x);
     		p2.y = Math.round(p2.y);
     		p3.x = Math.round(p3.x);
     		p3.y = Math.round(p3.y);
     		
     		if (isLine(unitsPerEm))
     			makeLine();
     		
     	}
    	//compute bounds for the bezier curve
     	void setBounds() {

        	//set initial bounds from end points
        	Line l = new Line(p0, p3);
        	Rect r = l.getBounds();
 
        	if ((p1.x < r.left) || (p1.x > r.right) || (p2.x < r.left) || (p2.x > r.right)) {
        		Limit lim = getBezLimit(p0.x, p1.x, p2.x, p3.x, r.left, r.right);
        		r.left = lim.min;
        		r.right = lim.max;
        	}
  
           	if ((p1.y < r.bottom) || (p1.y > r.top) || (p2.y < r.bottom) || (p2.y > r.top)) {
        		Limit lim = getBezLimit(p0.y, p1.y, p2.y, p3.y, r.bottom, r.top);
        		r.bottom = lim.min;
        		r.top = lim.max;
        	}
 
           	bounds = r;     		
     	}
     	
        Rect getBounds() {
        	
        	if (bounds != null)
        		return bounds;

        	setBounds();        	
            return bounds;   
            
        }

        private Point computeISectPoint(float t) {
        	float x = 	t*(t*(t*(p3.x - 3*(p2.x - p1.x) - p0.x) +
    				3*(p2.x - 2*p1.x + p0.x)) +
       			 3*(p1.x - p0.x)) + p0.x;
        	float y = t*(t*(t*(p3.y - 3*(p2.y - p1.y) - p0.y) +
    				3*(p2.y - 2*p1.y + p0.y)) +
       			 3*(p1.y - p0.y)) + p0.y;
        	return new Point(x, y);        	
        }
        
        private boolean isLine(float unitsPerEm) {
        	float a = p3.y - p0.y;
        	float b = p3.x - p0.x;
        	if (Math.abs(a) <= 1 && Math.abs(b) <= 1)
        		return true;	// Segment within unit square 
        	else
        		{
        		float s = a*(p1.x - p0.x) + b*(p0.y - p1.y);
        		float t = a*(p2.x - p0.x) + b*(p0.y - p2.y);
        		float tol = milliem(unitsPerEm, 1); 
        		return (s*s + t*t) < tol*tol*(a*a + b*b);
        		}
        	        	
        }
        
        private Line makeLine() {
        	Line l = new Line(p0.x, p0.y, p3.x, p3.y);
        	l.id = id;
        	l.t0 = t0;
        	l.t1 = t1;
        	l.origSeg = this.origSeg;
        	return l;
        }
        
        private List splitAtT(float t) {
        	List curveList = new ArrayList(2);
        	           	
           	float ap0x = p0.x;
           	float ap0y = p0.y;
        	float ap1x = t*(p1.x - p0.x) + ap0x;
        	float ap1y = t*(p1.y - p0.y) + ap0y;
        	float ap2x = t*t*(p2.x - 2*p1.x + p0.x) + 2*ap1x - ap0x;
        	float ap2y = t*t*(p2.y - 2*p1.y + p0.y) + 2*ap1y - ap0y;
        	float ap3x = t*t*t*(p3.x - 3*(p2.x - p1.x) - p0.x) + 
        		3*(ap2x - ap1x) + ap0x;
        	float ap3y = t*t*t*(p3.y - 3*(p2.y - p1.y) - p0.y) + 
        		3*(ap2y - ap1y) + ap0y;
        	
        	Curve a = new Curve(ap0x, ap0y, ap1x, ap1y, ap2x, ap2y, ap3x, ap3y);
        	a.t0 = 0; 
        	a.t1 = t;
        	a.origSeg = this.origSeg;
        	curveList.add(a);
        	
        	t = 1 - t;
         	float bp3x = p3.x;
        	float bp3y = p3.y;
        	float bp2x = t*(p2.x - p3.x) + bp3x;
        	float bp2y = t*(p2.y - p3.y) + bp3y;;
        	float bp1x =  t*t*(p1.x - 2*p2.x + p3.x) + 2*bp2x - bp3x;
        	float bp1y = t*t*(p1.y - 2*p2.y + p3.y) + 2*bp2y - bp3y;
         	float bp0x = ap3x;
           	float bp0y = ap3y;
       	
        	Curve b = new Curve(bp0x, bp0y, bp1x, bp1y, bp2x, bp2y, bp3x, bp3y);
        	b.t0 = t;  
        	b.t1 = 1;
        	b.origSeg = this.origSeg;
        	curveList.add(b);        	
                    	
        	return curveList;
        }
        
        // Split Bezier curve a at the midpoint into Bezier curves b and a. 
        private List splitMid() {
        	Point s = new Point();
        	Point t = new Point();
        	Point u = new Point();
        	Point c = new Point();
        	float ct;
        	List curveList = new ArrayList(2);
        	
        	// Compute intermediate points 
        	s.x = (p2.x + p3.x)/2;
        	s.y = (p2.y + p3.y)/2;
        	t.x = (p1.x + p2.x)/2;
        	t.y = (p1.y + p2.y)/2;
        	u.x = (p0.x + p1.x)/2;
        	u.y = (p0.y + p1.y)/2;
        	c.x = (s.x + 2*t.x + u.x)/4;
        	c.y = (s.y + 2*t.y + u.y)/4;
        	ct = (t0 + t1)/2;

        	// Compute first half of split (curve b) 
        	Curve curve = new Curve(p0.x, p0.y, u.x, u.y, (t.x + u.x)/2, (t.y + u.y)/2, c.x, c.y);
        	curve.t0 = t0;
        	curve.t1 = ct;
        	curve.depth = depth+1;
        	curve.origSeg = this.origSeg;
        	curveList.add(curve);

        	// Compute second half of split (curve a) 
           	curve = new Curve(c.x, c.y, (s.x + t.x)/2, (s.y + t.y)/2, s.x, s.y, p3.x, p3.y);
        	curve.t0 = ct;
        	curve.t1 = t1;
        	curve.depth = depth;
        	curve.origSeg = this.origSeg;
        	curveList.add(curve);
        	
        	return curveList;
 
        }
        
 
        // Return true if curve flat enough else false. 
        //Adapted from the flatten.c in Type 1 rasterizer. 
        private  boolean isFlatEnough(float unitsPerEm) {
       	
        	if (depth == 6)
        		// Split 6 times; bbox < .002 em; don't split 
        		return true;
        
        	if ((Math.abs(bounds.right - bounds.left) > milliem(unitsPerEm, 127)) ||
        			 (Math.abs(bounds.top - bounds.bottom) > milliem(unitsPerEm, 127))) {
        			// BBox eighth em or bigger, split
        			depth = 0;
        			return false;
        			
         	}
         	else if (((p0.x > p1.x || p1.x > p2.x || p2.x > p3.x) &&
         			  (p3.x > p2.x || p2.x > p1.x || p1.x > p0.x)) ||
         			 ((p0.y > p1.y || p1.y > p2.y || p2.y > p3.y) &&
         			  (p3.y > p2.y || p2.y > p1.y || p1.y > p0.y)))
         		// Points not monotonic in x and y; split 
         		return false;
         	else
         		{
         		// Split if control points not spaced evenly within .003 em 
         		float eps3 = milliem(unitsPerEm, 9);
         		float c1x = Math.abs(p1.x - p0.x);
         		float c3x = Math.abs(p3.x - p0.x);
         		if (Math.abs(c3x - 3*c1x) > eps3)
         			return false;
         		else
         			{
         			float c2x = Math.abs(p2.x - p0.x);
         			if (Math.abs(2*(c3x - c2x) - c2x) > eps3)
         				return false;
         			else
         				{
         				float c1y = Math.abs(p1.y - p0.y);
         				float c3y = Math.abs(p3.y - p0.y);
         				if (Math.abs(c3y - 3*c1y) > eps3)
         					return false;
         				else
         					{
         					float c2y = Math.abs(p2.y - p0.y);
         					if (Math.abs(2*(c3y - c2y) - c2y) > eps3)
         						return false;
         					}
         				}
         			}
         		}
         	return true;
         	        		
        }
        
        // Returns a list of float t values that are the extrema 
        private List findExtrema(float a0, float a1, float a2, float a3) {
        	float t[] = new float[2];
        	int i = 0;
        	int j = 0;
        	float a = a3 - 3*(a2 - a1) - a0;
        	float b = a2 - 2*a1 + a0;
        	float c = a1 - a0;

        	// Solve dy/dx=0 
        	if (a == 0)
        	{
        		if (b != 0)
        			t[i++] = -c/(2*b);
        	}
        	else
        	{
        		float r = b*b - a*c;
        		if (r >= 0)
        		{
        			r = (float)Math.sqrt(r);
        			t[i++] = (-b + r)/a;
        			t[i++] = (-b - r)/a;
        		}
        	}

        	// Select solutions in range 0 < t and t < 1 
        	float solns[] = new float[2];
        	for (i--; i >= 0; i--) {
        		if (0 < t[i] && t[i] < 1)
        			solns[j++] = t[i];
        	}

        	// Sort solutions 
        	if (j == 2 && solns[0] > solns[1])
        	{
        		solns[0] = t[1];
        		solns[1] = t[0];
        	}

        	List solnsList = new ArrayList();
        	for (i = 0; i < j; i++) {
        		Float d = new Float(solns[i]);
        		solnsList.add(d);
        	}
        	return solnsList;
        }

        // Find t value that yeilds the target value from Bezier equation. 
        private float solveAtValue(float value, float a3,  float a2, float a1, float a0) {
        	float a = a3 - 3*(a2 - a1) - a0;
        	float b = 3*(a2 - 2*a1 + a0);
        	float c = 3*(a1 - a0);
        	float lo = 0;
        	float hi = 1;
        	float t;

        	// Binary search for solution 
        	for (;;)
        	{
        		float delta;

        		t = (lo + hi)/2;
        		delta = t*(t*(t*a + b) + c) + a0 - value;
        		if (Math.abs(delta) < 0.01)
        			break;
        		else if (delta < 0)
        			lo = t;
        		else
        			hi = t;
        	}
        	return t;
        }


        // Solve segment for x value(s) at given y. 
        List solveAtY(float y)
        {

        	// Find extrema 
        	List tList = findExtrema(p3.y, p2.y, p1.y, p0.y);

        	// Split curve(s) 
        	List T0CurveList, T1CurveList;
        	Float t0, t1;
        	int cnt = tList.size();
        	Curve[] curves = new Curve[cnt+1];
        	if (cnt > 0) {
        		t0 = (Float) tList.get(0);
        		T0CurveList = splitAtT(t0.floatValue());
        		curves[0] = (Curve) T0CurveList.get(0);
        		curves[1] = (Curve) T0CurveList.get(1);
        		if (cnt > 1) {
        			t1 = (Float) tList.get(1);
        			float t = (t1.floatValue() - t0.floatValue())/(1 - t0.floatValue());
        			T1CurveList = curves[1].splitAtT(t);
        			curves[1] = (Curve) T1CurveList.get(0);
        			curves[2] = (Curve) T0CurveList.get(1);  
        		}
        	}
        	else {
        		//copy curve
        		curves[0] = new Curve(p0, p1, p2, p3);
        	}

        	List iceptList = new ArrayList();
        	for (int i = 0; i <= cnt; i++)
        	{
        		Point pnt0;
        		Point pnt1;
        		Point pnt2;
        		Point pnt3;

        		// Orient curve 
        		if (curves[i].p3.y > curves[i].p0.y)
        		{
        			pnt0 = curves[i].p0;
        			pnt1 = curves[i].p1;
        			pnt2 = curves[i].p2;
        			pnt3 = curves[i].p3;
        		}
        		else
        		{
        			pnt0 = curves[i].p3;
        			pnt1 = curves[i].p2;
        			pnt2 = curves[i].p1;
        			pnt3 = curves[i].p0;
        		}

        		int wind = curves[i].getWindingY();

        		float icpnt;
        		if (y < pnt0.y || y > pnt3.y)
        			continue;
        		else if (Math2.epsilonEquals(y, pnt0.y))
        			icpnt = pnt0.x;
        		else if (Math2.epsilonEquals(y, pnt3.y))
        			icpnt = pnt3.x;
        		else {
        			float t = solveAtValue(y, pnt3.y, pnt2.y, pnt1.y, pnt0.y);
        			icpnt = t*(t*(t*(pnt3.x - 3*(pnt2.x - pnt1.x) - pnt0.x) +
        					3*(pnt2.x - 2*pnt1.x + pnt0.x)) +
        					3*(pnt1.x - pnt0.x)) + pnt0.x;
        		}

        		ICept ic = new ICept(icpnt, wind);
        		iceptList.add(ic);
        	}

        	return iceptList;
        }

        // Solve segment for y value(s) at given x. 
        List solveAtX(float x)
        {
        	// Find extrema 
        	List tList = findExtrema(p3.x, p2.x, p1.x, p0.x);

        	// Split curve(s) 
        	List T0CurveList, T1CurveList;
        	Float t0, t1;
        	int cnt = tList.size();
        	Curve[] curves = new Curve[cnt+1];
        	if (cnt > 0) {
        		t0 = (Float) tList.get(0);
        		T0CurveList = splitAtT(t0.floatValue());
        		curves[0] = (Curve) T0CurveList.get(0);
        		curves[1] = (Curve) T0CurveList.get(1);
        		if (cnt > 1) {
        			t1 = (Float) tList.get(1);
        			float t = (t1.floatValue() - t0.floatValue())/(1 - t0.floatValue());
        			T1CurveList = curves[1].splitAtT(t);
        			curves[1] = (Curve) T1CurveList.get(0);
        			curves[2] = (Curve) T0CurveList.get(1);  
        		}
        	} 
        	else {
        		//copy curve
        		curves[0] = new Curve(p0, p1, p2, p3);
        	}

        	List iceptList = new ArrayList();
        	for (int i = 0; i <= cnt; i++)
        	{
        		Point pnt0;
        		Point pnt1;
        		Point pnt2;
        		Point pnt3;

        		// Orient curve 
        		if (curves[i].p3.x > curves[i].p0.x)
        		{
        			pnt0 = curves[i].p0;
        			pnt1 = curves[i].p1;
        			pnt2 = curves[i].p2;
        			pnt3 = curves[i].p3;
        		}
        		else
        		{
        			pnt0 = curves[i].p3;
        			pnt1 = curves[i].p2;
        			pnt2 = curves[i].p1;
        			pnt3 = curves[i].p0;
        		}

        		int wind = curves[i].getWindingX();

        		float icpnt;
        		if (x < pnt0.x || x > pnt3.x)
        			continue;
        		else if (Math2.epsilonEquals(x, pnt0.x))
        			icpnt = pnt0.y;
        		else if (Math2.epsilonEquals(x, pnt3.x))
        			icpnt = pnt3.y;
        		else {
        			float t = solveAtValue(x, pnt3.x, pnt2.x, pnt1.x, pnt0.x);
        			icpnt = t*(t*(t*(pnt3.y - 3*(pnt2.y - pnt1.y) - pnt0.y) +
        					3*(pnt2.y - 2*pnt1.y + pnt0.y)) +
        					3*(pnt1.y - pnt0.y)) + pnt0.y;
        		}

        		ICept ic = new ICept(icpnt, wind);
        		iceptList.add(ic);
        	}

        	return iceptList;

        }
        


        int getWindingX() {
        	return getWinding(p0.x, p3.x);
        }

        int getWindingY() {
        	return getWinding(p0.y, p3.y);
        }

    }
    
    private static abstract class Segment
    {    	
    	SubPath subPath;
      	int flags;
    	Segment nextSegment;
    	Segment prevSegment;
    	Segment origSeg;
    	   
    	abstract Rect getBounds();
    	
       	abstract void setBounds();
    	
       	abstract Point getLastPoint();
    	
    	abstract Point getFirstPoint();
    	
    	abstract int getID();
    	
    	abstract void roundControlPoints(float unitsPerEm);
    	
    	abstract boolean horizontal();
    	
    	abstract int getWindingX();
    	
    	abstract int getWindingY();
    	
    	abstract boolean equalControlPoints(Segment s);
     	
     	abstract boolean reverseControlPoints(Segment s);
     	
    	abstract Segment copySegment();
    	
     	abstract List solveAtX(float val);
     	
     	abstract List solveAtY(float val);
     	
     	abstract public String toString();
    	      	
    	private Segment nextSeg() {
    		return nextSegment;
    	}
    	
    	private Segment prevSeg() {
    		return prevSegment;
    	}
    	
    	private void link(Segment nextSeg)
        {
    		if (nextSeg == this) {
    			//starting a subpath
    			this.nextSegment = this;
    			this.prevSegment = this;
    		} else {
    			nextSeg.nextSegment = this.nextSegment;
    			this.nextSegment.prevSegment = nextSeg;
    			nextSeg.prevSegment = this;
    			this.nextSegment = nextSeg;
    		}
     	}
    	
    	private void relink(Segment nextSeg) 
    	{
    		this.nextSegment = nextSeg;
    		nextSeg.prevSegment = this;
    	}
    	
       	private void replace(Segment newSeg) {
    		newSeg.nextSegment = this.nextSegment;
    		newSeg.prevSegment = this.prevSegment;
    	}
    	
       	private void remove() {
       		this.prevSegment.nextSegment = this.nextSegment;
       		this.nextSegment.prevSegment = this.prevSegment;
       	}
       	
        // Round points in segment to nearest integer. 
        private void round(float unitsPerEm)
        {
        	roundControlPoints(unitsPerEm);
        	setBounds();
        }
    	
    	// Get winding for beg and end values. 
    	static int getWinding(float beg, float end) {
    		return (beg > end)? 1: -1;
    	}
    	
    	static int getWindingAtValue(float beg, float end, float value) {
    		if ((value < beg && value < end) || (value > beg && value > end))
    			return 0;
    		else
    			return (beg > end)? 1: -1;
    	}
    }

	private static class SegComparator implements Comparator, Serializable
    {   
	private static final long serialVersionUID = 1L;
    	public int compare(Object o1, Object o2) {
    		Intersect i1 = (Intersect) o1;
    		Intersect i2 = (Intersect) o2;
    		return i1.cmpSegs(i2);    		
    	}
    	
    	boolean equals(Object o1, Object o2) {
    		Intersect i1 = (Intersect) o1;
    		Intersect i2 = (Intersect) o2;
    		return (i1.cmpSegs(i2) == 0);        		
    	}
    }
    
    private static class IDComparator implements Comparator, Serializable
    {
	private static final long serialVersionUID = 1L;
    	public int compare(Object o1, Object o2) {
    		Intersect i1 = (Intersect) o1;
    		Intersect i2 = (Intersect) o2;
    		return i1.cmpIds(i2);    		
    	}
    	
    	boolean equals(Object o1, Object o2) {
    		Intersect i1 = (Intersect) o1;
    		Intersect i2 = (Intersect) o2;
    		return (i1.cmpIds(i2) == 0);        		
    	}
    }
    
    //  private class representing an intersection
    private class Intersect {
    	float t;				// t value for intercept on segment
    	Point p;				// intersection point 
    	Segment seg;			// original segment
    	Segment splitSeg;		// split segment
    	long id;				// Pair identifier 
    	
    	Intersect(float t, Point p, Segment seg, Segment splitSeg, long id) {
    		this.p = p;
    		this.t = t;
    		this.seg = seg;
    		this.splitSeg = splitSeg;
    		this.id = id;
    	}
    	
    	public String toString() {
    		String s = ("Isect point:" + p.toString() + ") t: " + t + "\n");
    		if (seg != null) s= s+ "Segment:" + seg.toString() +"\n";
    		if (splitSeg != null) s=s+"Split Segment:" + splitSeg.toString() + "\n";
    		return s;
    	}
    	
    	// Compare intersections by id. 
    	private int cmpIds(Intersect i2) {
  
     		if (id < i2.id)
    			return -1;
    		else if (id > i2.id)
    			return 1;
    		else
    			return 0;
    	}
    	
    	// Compare intersections by segments 
    	private int cmpSegs(Intersect i2) {
  
    		int iSeg=segList.indexOf(this.seg);
    		int i2Seg = segList.indexOf(i2.seg);
    		if (iSeg < i2Seg)
    			return -1;
    		else if (iSeg > i2Seg)
    			return 1;
    		else if (this.t < i2.t)
    			return -1;
    		else if (this.t > i2.t)
    			return 1;
    		else
    			return 0;    		
    		
    	}
    }
    
    
    //  private class representing a subpath
    private  class SubPath {
    	Rect bounds;
    	Point startPoint;
    	boolean pathISected;
    	Segment firstSegment;
    	Segment lastSegment;
    	int numSegments;
    	SubPath nextSubPath;
    	
    	SubPath() {
    		bounds = new Rect(0,0,0,0);
    		startPoint = null;
    		firstSegment = null;
    		lastSegment = null;
    		numSegments = 0;
    		nextSubPath = null;
    		pathISected = false;
     	}
    	
    	SubPath(float x, float y) {
    		bounds = new Rect(x,y,x,y);
    		startPoint = new Point(x, y);
    		firstSegment = null;
    		lastSegment = null;
       		numSegments = 0;
       		nextSubPath = null;
       		pathISected = false;
    	}
    	
    	public String toString() {
    		String str = "Num Segments:" + numSegments;
    		if (numSegments == 0) return str;
    		Segment s = firstSegment;
    		do {
    			str = str + s.toString() + "\n";
    			s = s.nextSeg();
    		} while (s != firstSegment);
    		return str;
    	}
    	
    	private int getNumSegments() {
    		return numSegments;
    	}
    	
    	private void addSegment(Segment s, boolean addToSegList) {
    		if (addToSegList)
    			segList.add(s);
    		s.subPath = this;
    		if (lastSegment == null) {
    			firstSegment = s;
    			lastSegment = s;
    			s.link(s);
    		} 
    		else {
    			lastSegment.link(s);
    			lastSegment = s;
    		}
    		bounds.grow(s.getBounds());
    		numSegments++;
    	}
    	
       	private void copySubPath(SubPath sp) throws ISectException {
    		Segment n = sp.firstSegment;;
   			if (sp.firstSegment.subPath == this) return;
   	   		int totalNumSegments = segList.size();
     		do {
       			Segment newseg = n.copySegment();
       			n = n.nextSeg();
    			addSegment(newseg, true);    			
    			if (this.numSegments > totalNumSegments) {
					//System.out.println("Exception: In AddSegments - inifinite loop");
			   		throw(new ISectException("error - infinite loop"));    				
    			}
    		} while (n != sp.firstSegment);
      	} 
    
    	// add segments s and all its subsequent segments
       	private void addSegments(Segment s) throws ISectException  {
    		Segment n;
    		int totalNumSegments = segList.size();
   			if (s.subPath == this) return;
   			n = s;
    		do {
        		n = s.nextSeg();
    			addSegment(s, false);
    			if (this.numSegments > totalNumSegments) {
					//System.out.println("Exception: In AddSegments - inifinite loop");
			   		throw(new ISectException("error - infinite loop"));    				
    			}
    			s = n;
    		} while (s.subPath != this);
      	}
       	
    	private void splitSegment(Segment seg, Segment newSeg) {
     		segList.add(newSeg);
    		newSeg.subPath = this;
    		seg.link(newSeg);
    		numSegments++;
    	}
    	
    	private void replaceSegment(Segment seg, Segment newSeg) {
    		segList.add(newSeg);
    		newSeg.subPath = this;
    		seg.replace(newSeg);
    	}
    	
    	private void removeSegment(Segment seg) throws ISectException {
    		if (seg.subPath == this) {
    			if (numSegments == 1) {
    				if (firstSegment == seg) {
    					firstSegment = null;
    					lastSegment = null;
    				} else {
    					//System.out.println("Exception: In removeSegment - error");
    			   		throw(new ISectException("error - cant handle"));
    				}

    			}
    			if (seg == firstSegment) {
    				firstSegment = seg.nextSeg();
    			}
    			if (seg == lastSegment) {
    				lastSegment = seg.prevSeg();
    			}
    			seg.remove();
    			numSegments--;
    		}
    			
    	}
    	    	   	
    	private void closepath(float cpx, float cpy) {
    		// draw a line from last point of last segment to start point
    		if (Math2.epsilonEquals(startPoint.x, cpx) && Math2.epsilonEquals(startPoint.y, cpy)) return;
    		Point lastPnt = new Point(cpx, cpy);
    		Line l = new Line(lastPnt, startPoint);
     		addSegment(l, true);
    	}
    	
    }

    float cpx, cpy;
    private SubPath currentSubPath;
    private List /* SubPath */ path;
    private List /* new SubPaths */ newPath;
    private List /* new SubPaths */ savedPath;   
    private List /*Intersect */ isectList;
    private List /*Segments */ segList;
    private float unitsPerEm;
    boolean selfIsectFlag;    
    OutlineConsumer oc;
    
    public NonOverlappingOutlineConsumer (OutlineConsumer oc, double upem) {
    	this.oc = oc;
    	currentSubPath = null;
    	path = new ArrayList();
    	segList = new ArrayList();
        cpx = 0;
        cpy = 0;
        unitsPerEm = (float) upem;
        
        //debug params
        debugNumGlyphsWithOverlaps = 0;
        debugNumFailures = 0;
    }
    
    // Save intersection. 
    private void saveIsect(Point p, Segment segment, float t, long id)
    	{	
    	Intersect newISect;


		// Search for insertion position 
    	ListIterator iSectIter = isectList.listIterator();
		Point u = new Point(Math.round(p.x), Math.round(p.y));
		float vx, vy;
		Intersect isect = null;
		if (!selfIsectFlag) {
			while (iSectIter.hasNext()) {
				isect = (Intersect) iSectIter.next(); 
				SubPath iSubPath = isect.seg.subPath;
				//long iPath = h->segs.array[isect->iSeg].iPath;
				if (iSubPath == segment.subPath) {
					vx = Math.round(isect.p.x);
					if (Math2.epsilonEquals(vx, u.x)) {
						vy = Math.round(isect.p.y);
						if (Math2.epsilonEquals(vy, u.y)) {
							return;	// Matches previous intersection; ignore 
						}
					}
				}
			}
		}
		
		// Insert new record 
		newISect = new Intersect(t, p, segment, null, id);
		isectList.add(newISect);
		
    	// Mark path as intersected 
    	segment.subPath.pathISected = true;
    }
    
    // Save intersection pair. 
    private void saveIsectPair(Segment seg0, float t0, Segment seg1, float t1)
    	{
    	Point p;
    	long id = isectList.size();
    	

    	// Compute intersection point 
    	if (t0 == 0)
    		p = new Point(seg0.getFirstPoint());
    	else if (t1 == 0)
    		p = new Point(seg1.getFirstPoint());
    	else if (t0 == 1) 
    		p = new Point(seg0.getLastPoint());
    	else if (t1 == 1)
    		p = new Point(seg1.getLastPoint());
    	else if (seg0 instanceof Line) {
     		Line l = (Line) seg0;
    		p = l.computeISectPoint(t0);
    	} 
       	else if (seg1 instanceof Line) {
     		Line l = (Line) seg1;
    		p = l.computeISectPoint(t1);
    	}else {
    		Curve c = (Curve) seg0;
    		p = c.computeISectPoint(t0);
    	}

    	// If intersection is very close to end of segment, force it to	end 
    	long x = Math.round(p.x);
    	long y = Math.round(p.y);
    	if (0 < t0 && t0 < 1) {
    		long segFirstpx = Math.round(seg0.getFirstPoint().x);
    		long segFirstpy = Math.round(seg0.getFirstPoint().y);
    		long segLastpx = Math.round(seg0.getLastPoint().x);
    		long segLastpy = Math.round(seg0.getLastPoint().y);
   	
    		if	((x == segFirstpx && y == segFirstpy) ||
    				(x == segLastpx && y == segLastpy))
    				t0 = Math.round(t0);
    	}
    	if (0 < t1 && t1 < 1) {
       		long segFirstpx = Math.round(seg1.getFirstPoint().x);
    		long segFirstpy = Math.round(seg1.getFirstPoint().y);
    		long segLastpx = Math.round(seg1.getLastPoint().x);
    		long segLastpy = Math.round(seg1.getLastPoint().y);
   	
    		if	((x == segFirstpx && y == segFirstpy) ||
    				(x == segLastpx && y == segLastpy))
    				t1 = Math.round(t1);
    	}

    	saveIsect(p, seg0, t0, id);
    	saveIsect(p, seg1, t1, id+1);
    	
    }

    // Intersect a line/line segments. 
    private void isectLineLineSegs(Segment s0, Segment s1)
    {
    	Line l0, l1;
    	if (s0 instanceof Line) 
    		l0 = (Line) s0;
    	else
    		return;
    	if (s1 instanceof Line) 
    		l1 = (Line) s1;
    	else
    		return;
    	
    	List iSects = l0.iSectLine(l1);
    	if (iSects.size() == 2) {
    		float[] t = (float[]) iSects.get(1);
    		saveIsectPair(s0, t[0], s1, t[1]);
    		t = (float[]) iSects.get(0);
    		saveIsectPair(s0, t[0], s1, t[1]);
    	} else if (iSects.size() == 1) {
       		float[] t = (float[]) iSects.get(0);
    		saveIsectPair(s0, t[0], s1, t[1]);
    	}
    		
    }
    
    // Intersect line/curve by recursive subdivision of curve. 
    private void isectLineCurveSegs(Segment s0, Segment s1) {
    	Line l;
    	Curve c;
    	if (s0 instanceof Line) 
    		l = (Line) s0;
    	else
    		return;
    	if (s1 instanceof Curve) 
    		c = (Curve) s1;
    	else
    		return;
    	
    	for (;;) {
    		if (c.isFlatEnough(unitsPerEm)) {
    			Line newLine = c.makeLine();
    			List iSects = newLine.iSectLine(l);
    			if (iSects.size() == 2) {
    				float[] t = (float[]) iSects.get(1);
       				saveIsectPair(s1.origSeg, 
							  c.t0 + t[0]*(c.t1 - c.t0),
							  s0.origSeg, 
							  l.t0 + t[1]*(l.t1 - l.t0));
       				t = (float[]) iSects.get(0);
    				saveIsectPair(s1.origSeg, 
    							  c.t0 + t[0]*(c.t1 - c.t0),
    							  s0.origSeg, 
    							  l.t0 + t[1]*(l.t1 - l.t0));
    				return;
     			} 
    			else if (iSects.size() == 1) {
    				float[] t = (float[]) iSects.get(0);
    				saveIsectPair(s1.origSeg, 
    							  c.t0 + t[0]*(c.t1 - c.t0),
    							  s0.origSeg, 
    							  l.t0 + t[1]*(l.t1 - l.t0));
    				return;
    			} else
    				return;
    		}
    		else if (l.iSectRect(c.getBounds())) {
    			// Split a into b and a 
    			List curves = c.splitMid();
    			Curve b = (Curve) curves.get(0);
    			c = (Curve) curves.get(1);
    			isectLineCurveSegs(l,b);
    		}
    		else
    			return;
    	}
    }
    
    // Intersect curve/curve by alternate recursive subdivision of curves. 
    private void isectCurveCurve(Curve a, Curve b)
    	{
    	if (!a.getBounds().overlap(b.getBounds()))
    		return;
    	else if (a.isFlatEnough(unitsPerEm)) {
    		Line l = a.makeLine();
     		isectLineCurveSegs(l, b);
    		}
    	else
    		{
    		// Split a into c and a 
    		List curves = a.splitMid();
    		Curve c = (Curve) curves.get(0); //first half
    	    a = (Curve) curves.get(1); //second half
    		isectCurveCurve(b, c);
    		isectCurveCurve(b, a);
    		}
    	}

    // Intersect a curve/curve segments. 
    private void isectCurveCurveSegs(Segment s0, Segment s1)
    	{
    	Curve c0, c1;
    	if (s0 instanceof Curve) 
    		c0 = (Curve) s0;
    	else
    		return;
    	if (s1 instanceof Curve) 
    		c1 = (Curve) s1;
    	else
    		return;
    	
    	if (c0.equalControlPoints(c1))
    		{
    		// Coincident identical curves; intersect endpoints 
    		saveIsectPair(s0, 0, s1, 0);
    		saveIsectPair(s0, 1, s1, 1);
    	}
    	else if (c0.reverseControlPoints(c1))
    		{
    		// Reversed coincident identical curves; intersect endpoints 
    		saveIsectPair(s0, 0, s1, 1);
    		saveIsectPair(s0, 1, s1, 0);
    	}
    	else
    	{
    		// Complex intersection 
    		isectCurveCurve(c0, c1);
    	}
    }

    // Intersect pair of segments. 
    private void isectSegPair(Segment s0, Segment s1)
    {
    	s0.origSeg = s0;
    	s1.origSeg = s1;
    	if (s0 instanceof Line)
    		{
    		if (s1 instanceof Line)
    			isectLineLineSegs(s0, s1);
    		else
    			isectLineCurveSegs(s0, s1);
    		}
    	else
    		{
    		if (s1 instanceof Line)
    			isectLineCurveSegs(s1, s0);
    		else
    			isectCurveCurveSegs(s0, s1);
    		}
    	}

    // Intersect pair of paths. 
    private void isectPathPair(SubPath p0, SubPath p1)
    {

    	Segment s0 = p0.firstSegment; 
    	if (s0 == null) return;
    	do {
    		Segment s1 = p1.firstSegment;
    		if (s1 == null) return;
        	do {
    			if (s0.getBounds().overlap(s1.getBounds()))
    				isectSegPair(s0, s1);
    			s1 = s1.nextSeg();
    		} while (s1 != p1.firstSegment);
    		s0 = s0.nextSeg();
    	} while (s0 != p0.firstSegment);
    }

    // Split segment at intersection point. 
    private void splitSegment(Intersect last, Intersect isect)
    {
    	float t = isect.t;
     	Segment seg = isect.seg;
    	Segment newSeg;

    	if (last != null && last.seg == isect.seg)
    	{
    		// Second or subsequent intercept on same segment 
    		if (t == last.t || t == 1) {
    			isect.splitSeg = last.splitSeg;
    			return;
    		}
    		else if (last.splitSeg != null)
    		{
    			t = (t - last.t)/(1 - last.t);
    			seg = last.splitSeg;
    			//isect.seg = seg;
    		}
    	}
    	else if (t == 0 || t == 1)
    		return;

    	// Split segment; allocate new segment 
    	if (seg instanceof Line)
    	{
       		// Split line segment 
       		Line l = (Line) seg;
    		Line newl = new Line(isect.p, l.p1);
    		l.p1.x = isect.p.x;
    		l.p1.y = isect.p.y;
    		
    		newSeg = newl;
    	}
    	else
    	{
    		// Split curve segment 
    		Curve c = (Curve) seg;
    		Point p0 = new Point(c.p0);
    		Point p1 = new Point(c.p1);
    		Point p2 = new Point(c.p2);
    		Point p3 = new Point(c.p3);

    		c.p1.x = Math.round(t*(p1.x - p0.x) + c.p0.x);
    		c.p1.y = Math.round(t*(p1.y - p0.y) + c.p0.y);
    		c.p2.x = Math.round(t*t*(p2.x - 2*p1.x + p0.x) + 2*c.p1.x - c.p0.x);
    		c.p2.y = Math.round(t*t*(p2.y - 2*p1.y + p0.y) + 2*c.p1.y - c.p0.y);
    		c.p3.x = Math.round(isect.p.x); 
    		c.p3.y = Math.round(isect.p.y);

    		if (c.isLine(unitsPerEm)) {
    			Line l = c.makeLine();
    			seg.subPath.replaceSegment(seg, l);    			
    		}

    		
    		t = 1 - t;
    		Point newp3 = new Point(p3);
    		Point newp2 = new Point(Math.round(t*(p2.x - p3.x) + newp3.x), 
    				Math.round(t*(p2.y - p3.y) + newp3.y));
    		Point newp1 = new Point(Math.round(t*t*(p1.x - 2*p2.x + p3.x) + 2*newp2.x - newp3.x), 
    				Math.round(t*t*(p1.y - 2*p2.y + p3.y) + 2*newp2.y - newp3.y));
    		Point newp0 = new Point(c.p3);
    		Curve newc = new Curve(newp0, newp1, newp2, newp3);
    		newSeg = newc;
    		if (newc.isLine(unitsPerEm)) {
    			Line l = newc.makeLine();
    			newSeg = l;
    		}
    	}

    	// Link new segment 
    	SubPath sp = seg.subPath;
    	sp.splitSegment(seg, newSeg);
    	isect.splitSeg = newSeg;
    }
 
    // Split intersecting segments. 
    private void splitIsectSegs()
    {
    	
    	// Sort list by segment 
    	SegComparator segCompare = new SegComparator();
    	Collections.sort(isectList, segCompare);
 
    	// Split segments 
    	Intersect last = null;
    	Iterator iter = isectList.iterator();
    	while (iter.hasNext()) {
    		Intersect isect = (Intersect) iter.next();
    		splitSegment(last, isect);
    		last = isect;
    	}
    	
    	// Update connection of multi-split segments 
    	Object[] isectArray = isectList.toArray(); 
    	for (int i = isectArray.length - 1; i > 0; i--)
    	{
    		last = (Intersect) isectArray[i - 1];
    		Intersect isect = (Intersect) isectArray[i];
    		if (isect.splitSeg != null && 
    			last != null && last.seg == isect.seg && last.splitSeg != null)
    		{
    			isect.seg = last.splitSeg;
    			isect.splitSeg =isect.seg.nextSeg();    			
    		}
    	}
    	
    	// Round split segments and update unsplit segment connections 
    	iter = isectList.iterator();
    	while (iter.hasNext()) {
    		Intersect isect = (Intersect) iter.next();
    		Segment seg = isect.seg;
    		if (isect.splitSeg == null) {
    			isect.splitSeg = (isect.t == 0)? seg.prevSeg(): seg.nextSeg();
    		} 
    		else 
    		{
    			seg.round(unitsPerEm);
    			isect.splitSeg.round(unitsPerEm);
    		}
    		
       		// Mark segments as intersected 
    		seg.flags |= SEG_ISECT;
    		isect.splitSeg.flags |= SEG_ISECT;
    		
    	}
    	
    }
    
    // Test segment winding and if non-zero delete it. 
    private void windTestSeg(Segment target)
    {
    	Point p = new Point();
    	
    	if ((target.flags & (SEG_WIND_TEST|SEG_DELETE)) != 0)
    		return;	// Ignore checked or deleted segments 

    	// Choose target point in middle of this segment 
    	if (target instanceof Line)
    	{
    		Line l = (Line) target;
    		p.x = (l.p0.x + l.p1.x)/2;
    		p.y = (l.p0.y + l.p1.y)/2;
    	}
    	else
    	{
    		Curve c = (Curve) target;
    		p.x = (c.p3.x + 3*(c.p2.x + c.p1.x) + c.p0.x)/8;
    		p.y = (c.p3.y + 3*(c.p2.y + c.p1.y) + c.p0.y)/8;
    	}

    	int windtotal = 0;
    	int windtarget;
    	boolean testvert = target.horizontal();

    	if (testvert)
    		windtarget = target.getWindingX(); 
    	else
    		windtarget = target.getWindingY();

    	SubPath sp = target.subPath;
    	do
    	{
     		
    		// Ignore paths that can't affect winding 
    		if (testvert)
    		{
    			if (sp.bounds.top < p.y ||
    					sp.bounds.left > p.x ||
    					sp.bounds.right <= p.x) {
    				sp = sp.nextSubPath;
    				continue;
    			}
    		}
    		else
    		{
    			if (sp.bounds.left > p.x ||
    					sp.bounds.bottom > p.y ||
    					sp.bounds.top <= p.y) {
    				sp = sp.nextSubPath;
       				continue;
    			}
    		}
    		
    		Segment seg = sp.firstSegment;
    		do
    		{

    			if (seg == target) {
    				;
    			} else if (testvert)
    			{
    				/* Consider only non-horizontal segments that are to the left
    				   of the target point, and cross, or touch from above, the
    				   horizontal line through the target point. */
    				Rect segBounds = seg.getBounds();
    				if (segBounds.top < p.y ||
    						segBounds.left > p.x ||
    						segBounds.right <= p.x ||
    						segBounds.left == segBounds.right) {
    					;
    				}else if (segBounds.bottom > p.y) {
    					windtotal += Segment.getWindingAtValue(seg.getFirstPoint().x, seg.getLastPoint().x, p.x);
     				} else if (seg.reverseControlPoints(target))
    				{
    					// Coincident in opposite direction 
    					target.flags |= (SEG_DELETE|SEG_WIND_TEST);
    					return;
    				}
    				else if (seg.equalControlPoints(target))
    				{
    					// Coincident in same direction 
    					if (path.indexOf(seg.subPath) < path.indexOf(target.subPath)) //is this the right things to do?
    						target.flags |= SEG_DELETE;
    					target.flags |= SEG_WIND_TEST;
    					return;
    				}
    				else
    				{
    					List iceptList = seg.solveAtX(p.x);
    					Iterator iter = iceptList.iterator();
    					while (iter.hasNext()) {
    						ICept icept = (ICept) iter.next();
    						if (icept.ic > p.y) {
    							windtotal += icept.winding;
    						}
     					}
    				}
    			}
    			else
    			{
    				/* Consider only non-vertical segments that are to the left
    				   of the target point, and cross, or touch from above, the
    				   horizontal line through the target point. */
    				Rect segBounds = seg.getBounds();
    				if (segBounds.left > p.x ||
    						segBounds.bottom > p.y ||
    						segBounds.top <= p.y ||
    						segBounds.top == segBounds.bottom) {
    					;
    				} else if (segBounds.right < p.x) {
    					windtotal += Segment.getWindingAtValue(seg.getFirstPoint().y, seg.getLastPoint().y, p.y);
    				} else if (seg.reverseControlPoints(target))
    				{
    					// Coincident in opposite direction 
    					target.flags |= (SEG_DELETE|SEG_WIND_TEST);
       					return;
    				}
    				else if (seg.equalControlPoints(target))
    				{
    					// Coincident in same direction 
    					if (path.indexOf(seg.subPath) < path.indexOf(target.subPath))  
    						target.flags |= SEG_DELETE;
    					target.flags |= SEG_WIND_TEST;
    					return;
    				}
    				else
    				{
     					List iceptList = seg.solveAtY(p.y);
     					Iterator iter = iceptList.iterator();
    					while (iter.hasNext()) {
    						ICept icept = (ICept) iter.next();
    						if (icept.ic < p.x) {
    							windtotal += icept.winding;
    						}
    					}
    				}
    			}
    			seg = seg.nextSeg();
    		} while (seg != sp.firstSegment);

    		sp = sp.nextSubPath;
    		
   		} while (sp != target.subPath);
    	
       	if ((windtotal != 0) && (windtotal + windtarget)!= 0)
   	 		target.flags |= SEG_DELETE;

    	target.flags |= SEG_WIND_TEST;

   	}


    // Delete overlapped segments or those with bad winding. 
    private void deleteBadSegs() throws ISectException
    {
    	if ((isectList.size() % 2) != 0) { //has to be even 
			//System.out.println("Exception: In deleteBadSegs - error - uneven intersects");
    		throw(new ISectException("error - uneven intersects"));
    	}
    	
    	// Sort list by id 
    	IDComparator idCompare = new IDComparator();
    	Collections.sort(isectList, idCompare);
		
    	// Delete badly wound segments from each intersection 
    	Iterator iter = isectList.iterator();
    	while (iter.hasNext()) {
    		Intersect isect = (Intersect) iter.next();
     		windTestSeg(isect.seg);
    		windTestSeg(isect.splitSeg);
    	}
    	

    	// Link non-deleted segments from each intersection 
    	iter = isectList.iterator();
    	while (iter.hasNext()) {
    		Segment abeg, aend, bbeg, bend;

    		Intersect a = (Intersect) iter.next();
    		Intersect b = (Intersect) iter.next();
    		
    		// Order intersection path "a" from beg to end 
    		if (a.seg.nextSeg() == a.splitSeg)
    		{
 	     		abeg = a.seg;
    			aend = a.splitSeg;
     		}
    		else
    		{
     			abeg = a.splitSeg;
    			aend = a.seg;
     		}

    		/* Order intersection path "b" from beg to end */
    		if (b.seg.nextSeg() == b.splitSeg)
    		{
     			bbeg = b.seg;
    			bend = b.splitSeg;
    		}
    		else
    		{
    			bbeg = b.splitSeg;
    			bend = b.seg;
    		}
   		
    		/* Link overlapping paths. There are 4 segments on 2 paths surrounding
    		   an intersection. Some, or all, of these segments may have been
    		   deleted because of bad winding. This switch determines how to link
    		   the remaining undeleted segments. Apart from cases 0, 3, 12, and 15
    		   which require no action the only other senisible cases are 6 and 9.
    		   However, the remaining cases may be encountered if the winding test
    		   fails due the numeric instability, which although rare, must be
    		   handled. Fortunately, it is possible to determine where the failure
    		   occured and fix it, yeilding a correct result. */
    		switch ((abeg.flags & SEG_DELETE)<<3 |
       				(aend.flags & SEG_DELETE)<<2 |
       				(bbeg.flags & SEG_DELETE)<<1 |
       				(bend.flags & SEG_DELETE)<<0) {

    				case 0:
    				case 3:
    				case 12:
    				case 15:
    					// Various cases requiring no action 
    					break;
    				case 5:
    				case 10:
    					// These cases shouldn't happen; give up 
    					//System.out.println("Exception: illegal segment flags");
    					throw(new ISectException("error - illegal segment flags"));
    				case 1:
    					abeg.flags |= SEG_DELETE;
    					bbeg.relink(aend);
    					break;
    				case 2:
    					aend.flags |= SEG_DELETE;
    					abeg.relink(bend);
    					break;
    				case 4:
    					bbeg.flags |= SEG_DELETE;
    					abeg.relink(bend);
    					break;
    				case 6:
    					abeg.relink(bend);
    					break;
    				case 7:
    					if (abeg.getLastPoint().equals(bend.getFirstPoint())) {
    						bend.flags &= ~SEG_DELETE;
    						abeg.relink(bend);
    					}
    					break;
    				case 8:
    					bend.flags |= SEG_DELETE;
    					bbeg.relink(aend);
    					break;
    				case 9:
    					bbeg.relink(aend);
    					break;
    				case 11:
    					if (bbeg.getLastPoint().equals(aend.getFirstPoint())) {
    						bbeg.flags &= ~SEG_DELETE;
    						bbeg.relink(aend);
    					}
    					break;
    				case 13:
    					if (bbeg.getLastPoint().equals(aend.getFirstPoint())) {
    						aend.flags &= ~SEG_DELETE;
    						bbeg.relink(aend);
    					}
    					break;
    				case 14:
    					if (abeg.getLastPoint().equals(bend.getFirstPoint())) {
    						abeg.flags &= ~SEG_DELETE;
    						abeg.relink(bend);
						}
    					break;
    		}
    	}
    }

    

    /* Create new path starting from seg. Path bounds and links must be set by
       caller. Returns pointer to new sub path. */
    private SubPath newSubPath(Segment seg) throws ISectException
    {
    	SubPath sp = new SubPath();
    	sp.addSegments(seg);
     	return sp;
    }

    // Build new sub path from intersection segment. 
    private void buildSubPath(Segment seg) throws ISectException
    {
 
    	if (((seg.flags & SEG_DELETE) != 0) ||	// Segment deleted 
    		newPath.contains(seg.subPath))	    // Segment already added to new path 
    		return;
    	
    	SubPath subPath = newSubPath(seg);
    	addPath(newPath, subPath);
    }

    // Build new path list. 
    private void buildNewPaths() throws ISectException
    {

    	newPath = new ArrayList();
    	
    	// Copy non-intersected paths to new list 
    	Iterator iter = path.iterator();
    	while (iter.hasNext()) {
    		SubPath sp = (SubPath) (iter.next());
    		if (!(sp.pathISected)) {
    			addPath(newPath, sp);
    		}
    	}

    	// Build new paths from intersection data 
    	iter = isectList.iterator();
    	while (iter.hasNext()) {
    		Intersect isect = (Intersect) iter.next();
    		buildSubPath(isect.seg);
    		buildSubPath(isect.splitSeg);
    	}
    	
    	// Use original path start points with new paths if possible 
    	iter = path.iterator();
    	while (iter.hasNext()) {
    		SubPath sp = (SubPath) (iter.next());
    		if ((sp.pathISected)) {
    			Segment seg = sp.firstSegment;
    			SubPath segsp = seg.subPath;
    			if (newPath.contains(segsp) && (seg.flags & SEG_DELETE) != 1) {
    				segsp.firstSegment = seg;
    				segsp.lastSegment = seg.prevSegment;
     			}
       		}
    	}
       	
    	// Coalesce co-linear line segments 
       	iter = newPath.iterator();
    	while (iter.hasNext()) {
    		SubPath sp = (SubPath) iter.next();
    		
    		int lastdir = 0;
    		int segcnt = 0;
    		Segment seg = sp.firstSegment;

    		do {
    		    int thisdir = 0;
    			if (seg instanceof Line) {
    				Line l = (Line) seg;
    				if (Math2.epsilonEquals(l.p0.x, l.p1.x))
    					thisdir = 1;
    				else if (Math2.epsilonEquals(l.p0.y, l.p1.y))
    					thisdir = -1;
    				else {
      	    			if (++segcnt > segList.size()) {
      						//System.out.println("Exception: In buildNewPath - segcnt > segList.size() detected infinite loop condition");
       	    				// Infinite loop! 
       	    				throw(new ISectException("Infinite loop condition!"));	
      	    			}
       	    			lastdir = thisdir;
          	    		seg = seg.nextSeg();
      	    			continue;
    				}
       				if (seg != sp.firstSegment && thisdir == lastdir)
					{
       					// Remove this segment from path 
       					Point p = new Point(l.p1);
       					Segment lastSeg = seg.prevSegment; 
       					sp.removeSegment(seg);
       					if (lastSeg instanceof Line) {
       						Line ll = (Line) lastSeg;
       						ll.p1 = p;
       						ll.setBounds();
       					} else {
       						Curve c = (Curve) lastSeg;
       						c.p3 = p;
       						c.setBounds();
       					}
 					}
       		   		      	    			
       				if (++segcnt >  segList.size()) {
						//System.out.println("Exception: In buildNewPath - detected infinite loop condition");
       	    			// Infinite loop! 
       					throw(new ISectException("Infinite loop condition!"));	
       				}
      	    		       	       				
    			}
   	    		lastdir = thisdir;
  	    		seg = seg.nextSeg();
    		} while (seg != sp.firstSegment);    		
    	}
    	  	
    }


    
	// Self-intersect path segments
	private void selfIsectPath(SubPath subPath) throws ISectException {
		
		if (subPath.getNumSegments() > MAX_NUM_SEGMENTS) {
			//System.out.println("Exception: In selfISectPath - too many segments");
	   		throw(new ISectException("error - too many segments in subpath"));
		}

		if (subPath.getNumSegments() < 3) return;		

		for (Segment s0 = subPath.firstSegment; s0.nextSeg() != subPath.lastSegment; s0 = s0.nextSeg())
		{
			Segment s1 = s0.nextSeg();
			do {

				s1 = s1.nextSeg();
				
				if (s1.nextSeg() != s0 && s0.getBounds().overlap(s1.getBounds()))
					isectSegPair(s0, s1);

			} while (s1 != subPath.lastSegment);

		}
		
	}
	
	// intersect glyph paths
	private void isectPath() throws ISectException {

		List subPathList = path;
		if (subPathList == null) return;
		if (subPathList.size() > MAX_NUM_SUBPATHS) {
			//System.out.println("Exception: In isectPath - too many subpaths");
	   		throw(new ISectException("error - too many subpaths"));
		}
		
		isectList = new ArrayList();
		
		//look for self-intersecting paths
		Iterator subPathListIter = subPathList.iterator();
		selfIsectFlag = true;
		while (subPathListIter.hasNext()) {
			SubPath subPath = (SubPath) subPathListIter.next();
			selfIsectPath(subPath);
		}

		//look for intersections between paths
		selfIsectFlag = false; 
    	Object[] subPathArray = subPathList.toArray();
		for (int i = 0; i < subPathArray.length; i++) {
			SubPath subPathi = (SubPath) subPathArray[i];
			for (int j = i+1; j < subPathArray.length; j++) {
				SubPath subPathj = (SubPath) subPathArray[j];
				if (subPathj.bounds.overlap(subPathi.bounds))
					isectPathPair(subPathi, subPathj);
			}
		}
	
		if (isectList.size() > 0) {
			// paths contained intersecting paths
			savedPath = copyPath(path);
			splitIsectSegs();
			deleteBadSegs();
			buildNewPaths();
			debugNumGlyphsWithOverlaps++;
		} else
			newPath = path;

		//debugPrint("number of intersecting glyphs:" + debugNumGlyphsWithOverlaps);

	}
	
	private void addPath(List pathList, SubPath subPath) {
		int size = pathList.size();
		if (size > 0) {
			SubPath lastsp = (SubPath) pathList.get(size - 1);
			subPath.nextSubPath = lastsp.nextSubPath;
			lastsp.nextSubPath = subPath;
		}
		else
		{
			//add as first element of path - keep as circular list
			subPath.nextSubPath = subPath;
		}
		pathList.add(subPath);

	}
	
	private List copyPath(List pathList) throws ISectException {
		List copiedPathList = new ArrayList();
		Iterator pathIter = pathList.iterator();
		while (pathIter.hasNext()) {
			SubPath sp = (SubPath) pathIter.next();
			SubPath newsp = new SubPath();
			newsp.copySubPath(sp);
			copiedPathList.add(newsp);
		}
		return copiedPathList;
	}
	
	private void generateOutline(List path) {
		if (path == null) return;
		Iterator pathListIter = path.iterator();
		if (savedPath != null) {
			boolean foundLoopInPath = false;
			int maxNumSegments = segList.size();
			while (pathListIter.hasNext() && !foundLoopInPath){
				SubPath subPath = (SubPath) pathListIter.next();
				if (subPath == null) continue;
				Segment pathSegment = subPath.firstSegment;
				if (pathSegment == null) return;
				int i = 0;
				do {
					i++;
					if (i > maxNumSegments) {
						path = savedPath;
						foundLoopInPath = true;
					}
					pathSegment = pathSegment.nextSeg();	
				} while ((pathSegment != subPath.firstSegment) && (!foundLoopInPath));
			}
		}
		
		pathListIter = path.iterator();		
		while (pathListIter.hasNext()) {
			SubPath subPath = (SubPath) pathListIter.next();
			if (subPath == null) continue;
			Segment pathSegment = subPath.firstSegment;
			if (pathSegment == null) return;
			boolean move = true;
			do {
				if (pathSegment instanceof Line) {
					Line l = (Line) pathSegment;
					if (move) {
						oc.moveto(l.p0.x, l.p0.y);
						move = false;
					}
					if (pathSegment != subPath.lastSegment) {//ignore last one; it was added to close the path
						oc.lineto(l.p1.x, l.p1.y);
					}
				       
				} else if (pathSegment instanceof Curve) {
					Curve c = (Curve) pathSegment;
					if (move) {
						oc.moveto(c.p0.x, c.p0.y);
						move = false;
					}
					oc.curveto(c.p1.x, c.p1.y, c.p2.x, c.p2.y, c.p3.x, c.p3.y);					
				}
				pathSegment = pathSegment.nextSeg();	
			} while (pathSegment != subPath.firstSegment);
		}

	}

    public void setMatrix(Matrix newMatrix) {
     	oc.setMatrix(newMatrix);
    }
     
    public void moveto (double x, double y) {
        if ((currentSubPath != null) && (currentSubPath.getNumSegments() > 0)) {
        	currentSubPath.closepath(cpx, cpy);
			addPath(path, currentSubPath);
        }
        currentSubPath = new SubPath((float)x, (float)y);
        cpx = (float)x;
        cpy = (float)y;
               
    }

    public void lineto (double x, double y) {
    	
    	if ((x == cpx) && (y == cpy)) return; // ignore 0 length lines
    	
        if (currentSubPath == null) 
        	moveto(0,0);
        Line l = new Line(cpx, cpy, (float)x,(float)y);
        currentSubPath.addSegment(l, true);
        cpx = (float) x;
        cpy = (float) y;
    }

    public void curveto (double x1, double y1, double x2, double y2) {
      curveto (Math.round ((cpx + 2*x1)/3.0), Math.round ((cpy + 2*y1)/3.0), 
          Math.round ((2*x1 + x2)/3.0), Math.round ((2*y1 + y2)/3.0),
          Math.round(x2), Math.round(y2));
    }         

    public void curveto (double x2, double y2, double x3, double y3, double x4, double y4) {
        if (currentSubPath == null) 
        	moveto(0,0);
        Curve c = new Curve(cpx, cpy, (float)x2, (float)y2, (float)x3, (float)y3, (float)x4,(float) y4);
        currentSubPath.addSegment(c, true);
        cpx = (float)x4;
        cpy = (float)y4;
    }
    
    public void reset()
    {
    	currentSubPath = null;    	
    	path.clear();
    	savedPath = null;
    	newPath = null;
    	isectList = null;
    	segList.clear();
        cpx = 0;
        cpy = 0;
    }

	public void endchar() 
	{
		if ((currentSubPath != null) && (currentSubPath.getNumSegments() > 0)) {
			currentSubPath.closepath(cpx, cpy);
			addPath(path, currentSubPath);
		}
					
		try {
			isectPath();
		} catch (ISectException e) {
			debugNumFailures++;
			newPath = savedPath;
			//debugPrint("number of failures:" + debugNumFailures);
		}
		generateOutline(newPath);
		
		oc.endchar();

		reset();
	}
	
    //debug parameters
    int debugNumGlyphsWithOverlaps;
    int debugNumFailures;
   
	public int getNumErrors() {
		return debugNumFailures;
	}
	
	/*
	 * DEBUGGING SUPPORT
	 */
	/*
	private static boolean DEBUG = true;

	private void debugPrint(String str) {
		if (DEBUG)
			System.out.println(str);
	}
	
	
	private void debugPrintPath(List path) {
		
		
		if (!DEBUG) return;
		
		System.out.println("Path - number of subpaths:" + path.size());
		Iterator pathIter = path.iterator();
		while (pathIter.hasNext()) {
			SubPath subPath = (SubPath) pathIter.next();
			System.out.println("Subpath segment nums:" + subPath.getNumSegments());
			System.out.println(subPath.toString());
			System.out.println("--------");
		}
		System.out.println("------------------------------------");

	}
	
	private void debugPrintISect() {
		
		if (!DEBUG) return;
		
		System.out.println("Number of ISects:" + isectList.size());
		Iterator iter = isectList.iterator();
		while (iter.hasNext()) {
			Intersect isect = (Intersect) iter.next();
			System.out.println(isect.toString());

		}
		System.out.println("---------------");

	}
	*/
	
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy