def calcCubicBounds(pt1, pt2, pt3, pt4):
    """Return the bounding rectangle for a cubic bezier segment.
    pt1 and pt4 are the "anchor" points, pt2 and pt3 are the "handles".

        >>> calcCubicBounds((0, 0), (25, 100), (75, 100), (100, 0))
        (0, 0, 100, 75.0)
        >>> calcCubicBounds((0, 0), (50, 0), (100, 50), (100, 100))
        (0.0, 0.0, 100, 100)
        >>> print("%f %f %f %f" % calcCubicBounds((50, 0), (0, 100), (100, 100), (50, 0)))
        35.566243 0.000000 64.433757 75.000000
    """
    (ax, ay), (bx, by), (cx,
                         cy), (dx,
                               dy) = calcCubicParameters(pt1, pt2, pt3, pt4)
    # calc first derivative
    ax3 = ax * 3.0
    ay3 = ay * 3.0
    bx2 = bx * 2.0
    by2 = by * 2.0
    xRoots = [t for t in solveQuadratic(ax3, bx2, cx) if 0 <= t < 1]
    yRoots = [t for t in solveQuadratic(ay3, by2, cy) if 0 <= t < 1]
    roots = xRoots + yRoots

    points = [(ax * t * t * t + bx * t * t + cx * t + dx,
               ay * t * t * t + by * t * t + cy * t + dy)
              for t in roots] + [pt1, pt4]
    return calcBounds(points)
Beispiel #2
0
def calcCubicBounds(pt1, pt2, pt3, pt4):
    """Calculates the bounding rectangle for a quadratic Bezier segment.

    Args:
        pt1,pt2,pt3,pt4: Control points of the Bezier as 2D tuples.

    Returns:
        A four-item tuple representing the bounding rectangle ``(xMin, yMin, xMax, yMax)``.

    Example::

        >>> calcCubicBounds((0, 0), (25, 100), (75, 100), (100, 0))
        (0, 0, 100, 75.0)
        >>> calcCubicBounds((0, 0), (50, 0), (100, 50), (100, 100))
        (0.0, 0.0, 100, 100)
        >>> print("%f %f %f %f" % calcCubicBounds((50, 0), (0, 100), (100, 100), (50, 0)))
        35.566243 0.000000 64.433757 75.000000
    """
    (ax, ay), (bx, by), (cx,
                         cy), (dx,
                               dy) = calcCubicParameters(pt1, pt2, pt3, pt4)
    # calc first derivative
    ax3 = ax * 3.0
    ay3 = ay * 3.0
    bx2 = bx * 2.0
    by2 = by * 2.0
    xRoots = [t for t in solveQuadratic(ax3, bx2, cx) if 0 <= t < 1]
    yRoots = [t for t in solveQuadratic(ay3, by2, cy) if 0 <= t < 1]
    roots = xRoots + yRoots

    points = [(
        ax * t * t * t + bx * t * t + cx * t + dx,
        ay * t * t * t + by * t * t + cy * t + dy,
    ) for t in roots] + [pt1, pt4]
    return calcBounds(points)
Beispiel #3
0
def calcQuadraticBounds(pt1, pt2, pt3):
    """Calculates the bounding rectangle for a quadratic Bezier segment.

    Args:
        pt1: Start point of the Bezier as a 2D tuple.
        pt2: Handle point of the Bezier as a 2D tuple.
        pt3: End point of the Bezier as a 2D tuple.

    Returns:
        A four-item tuple representing the bounding rectangle ``(xMin, yMin, xMax, yMax)``.

    Example::

        >>> calcQuadraticBounds((0, 0), (50, 100), (100, 0))
        (0, 0, 100, 50.0)
        >>> calcQuadraticBounds((0, 0), (100, 0), (100, 100))
        (0.0, 0.0, 100, 100)
    """
    (ax, ay), (bx, by), (cx, cy) = calcQuadraticParameters(pt1, pt2, pt3)
    ax2 = ax * 2.0
    ay2 = ay * 2.0
    roots = []
    if ax2 != 0:
        roots.append(-bx / ax2)
    if ay2 != 0:
        roots.append(-by / ay2)
    points = [(ax * t * t + bx * t + cx, ay * t * t + by * t + cy)
              for t in roots if 0 <= t < 1] + [pt1, pt3]
    return calcBounds(points)
Beispiel #4
0
 def recalcBounds(self, glyfTable):
     coordinates, endPts, flags = self.getCoordinates(glyfTable)
     if len(coordinates) > 0:
         self.xMin, self.yMin, self.xMax, self.yMax = calcBounds(
             coordinates)
     else:
         self.xMin, self.yMin, self.xMax, self.yMax = (0, 0, 0, 0)
Beispiel #5
0
 def _get_controlPointBounds(self):
     from fontTools.misc.transform import Transform
     # storage
     glyphRects = {}
     componentReferences = {}
     # scan loaded glyphs
     for glyphName, glyph in self._glyphs.items():
         if glyphName in self._scheduledForDeletion:
             continue
         glyphRect = glyph.controlPointBounds
         if glyphRect:
             glyphRects[glyphName] = glyphRect
     # scan glyphs that have not been loaded
     if self._glyphSet is not None:
         for glyphName, fileName in self._glyphSet.contents.items():
             if glyphName in self._glyphs or glyphName in self._scheduledForDeletion:
                 continue
             glif = self._glyphSet.getGLIF(glyphName)
             points, components = _fetchControlPointBoundsData(glif)
             if points:
                 glyphRects[glyphName] = calcBounds(points)
             for base, transformation in components:
                 xScale, xyScale, yxScale, yScale, xOffset, yOffset = transformation
                 if glyphName not in componentReferences:
                     componentReferences[glyphName] = []
                 componentReferences[glyphName].append((base, xScale, xyScale, yxScale, yScale, xOffset, yOffset))
     # get the transformed component bounding boxes and update the glyphs
     for glyphName, components in componentReferences.items():
         glyphRect = glyphRects.get(glyphName, (None, None, None, None))
         # XXX this doesn't handle nested components
         for base, xScale, xyScale, yxScale, yScale, xOffset, yOffset in components:
             # base glyph doesn't exist
             if base not in glyphRects:
                 continue
             baseRect = glyphRects[base]
             # base glyph has no points
             if None in baseRect:
                 continue
             # transform the base rect
             transform = Transform(xx=xScale, xy=xyScale, yx=yxScale, yy=yScale, dx=xOffset, dy=yOffset)
             xMin, yMin, xMax, yMax = baseRect
             (xMin, yMin), (xMax, yMax) = transform.transformPoints([(xMin, yMin), (xMax, yMax)])
             componentRect = (xMin, yMin, xMax, yMax)
             # update the glyph rect
             if None in glyphRect:
                 glyphRect = componentRect
             else:
                 glyphRect = unionRect(glyphRect, componentRect)
         # store the updated rect
         glyphRects[glyphName] = glyphRect
     # work out the unified rect
     fontRect = None
     for glyphRect in glyphRects.values():
         if fontRect is None:
             fontRect = glyphRect
         elif glyphRect is not None:
             fontRect = unionRect(fontRect, glyphRect)
     # done
     return fontRect
	def recalcBounds(self, glyfTable):
		coords, endPts, flags = self.getCoordinates(glyfTable)
		if len(coords) > 0:
			if 0:
				# This branch calculates exact glyph outline bounds
				# analytically, handling cases without on-curve
				# extremas, etc.  However, the glyf table header
				# simply says that the bounds should be min/max x/y
				# "for coordinate data", so I suppose that means no
				# fancy thing here, just get extremas of all coord
				# points (on and off).  As such, this branch is
				# disabled.

				# Collect on-curve points
				onCurveCoords = [coords[j] for j in range(len(coords))
						 if flags[j] & flagOnCurve]
				# Add implicit on-curve points
				start = 0
				for end in endPts:
					last = end
					for j in range(start, end + 1):
						if not ((flags[j] | flags[last]) & flagOnCurve):
							x = (coords[last][0] + coords[j][0]) / 2
							y = (coords[last][1] + coords[j][1]) / 2
							onCurveCoords.append((x,y))
						last = j
					start = end + 1
				# Add bounds for curves without an explicit extrema
				start = 0
				for end in endPts:
					last = end
					for j in range(start, end + 1):
						if not (flags[j] & flagOnCurve):
							next = j + 1 if j < end else start
							bbox = calcBounds([coords[last], coords[next]])
							if not pointInRect(coords[j], bbox):
								# Ouch!
								warnings.warn("Outline has curve with implicit extrema.")
								# Ouch!  Find analytical curve bounds.
								pthis = coords[j]
								plast = coords[last]
								if not (flags[last] & flagOnCurve):
									plast = ((pthis[0]+plast[0])/2, (pthis[1]+plast[1])/2)
								pnext = coords[next]
								if not (flags[next] & flagOnCurve):
									pnext = ((pthis[0]+pnext[0])/2, (pthis[1]+pnext[1])/2)
								bbox = calcQuadraticBounds(plast, pthis, pnext)
								onCurveCoords.append((bbox[0],bbox[1]))
								onCurveCoords.append((bbox[2],bbox[3]))
						last = j
					start = end + 1

				self.xMin, self.yMin, self.xMax, self.yMax = calcIntBounds(onCurveCoords)
			else:
				self.xMin, self.yMin, self.xMax, self.yMax = calcIntBounds(coords)
		else:
			self.xMin, self.yMin, self.xMax, self.yMax = (0, 0, 0, 0)
Beispiel #7
0
	def recalcBounds(self, glyfTable):
		coords, endPts, flags = self.getCoordinates(glyfTable)
		if len(coords) > 0:
			if 0:
				# This branch calculates exact glyph outline bounds
				# analytically, handling cases without on-curve
				# extremas, etc.  However, the glyf table header
				# simply says that the bounds should be min/max x/y
				# "for coordinate data", so I suppose that means no
				# fancy thing here, just get extremas of all coord
				# points (on and off).  As such, this branch is
				# disabled.

				# Collect on-curve points
				onCurveCoords = [coords[j] for j in range(len(coords))
						 if flags[j] & flagOnCurve]
				# Add implicit on-curve points
				start = 0
				for end in endPts:
					last = end
					for j in range(start, end + 1):
						if not ((flags[j] | flags[last]) & flagOnCurve):
							x = (coords[last][0] + coords[j][0]) / 2
							y = (coords[last][1] + coords[j][1]) / 2
							onCurveCoords.append((x,y))
						last = j
					start = end + 1
				# Add bounds for curves without an explicit extrema
				start = 0
				for end in endPts:
					last = end
					for j in range(start, end + 1):
						if not (flags[j] & flagOnCurve):
							next = j + 1 if j < end else start
							bbox = calcBounds([coords[last], coords[next]])
							if not pointInRect(coords[j], bbox):
								# Ouch!
								warnings.warn("Outline has curve with implicit extrema.")
								# Ouch!  Find analytical curve bounds.
								pthis = coords[j]
								plast = coords[last]
								if not (flags[last] & flagOnCurve):
									plast = ((pthis[0]+plast[0])/2, (pthis[1]+plast[1])/2)
								pnext = coords[next]
								if not (flags[next] & flagOnCurve):
									pnext = ((pthis[0]+pnext[0])/2, (pthis[1]+pnext[1])/2)
								bbox = calcQuadraticBounds(plast, pthis, pnext)
								onCurveCoords.append((bbox[0],bbox[1]))
								onCurveCoords.append((bbox[2],bbox[3]))
						last = j
					start = end + 1

				self.xMin, self.yMin, self.xMax, self.yMax = calcIntBounds(onCurveCoords)
			else:
				self.xMin, self.yMin, self.xMax, self.yMax = calcIntBounds(coords)
		else:
			self.xMin, self.yMin, self.xMax, self.yMax = (0, 0, 0, 0)
Beispiel #8
0
def calcQuadraticBounds(pt1, pt2, pt3):
    """Return the bounding rectangle for a qudratic bezier segment.
    pt1 and pt3 are the "anchor" points, pt2 is the "handle".

        >>> calcQuadraticBounds((0, 0), (50, 100), (100, 0))
        (0, 0, 100, 50.0)
        >>> calcQuadraticBounds((0, 0), (100, 0), (100, 100))
        (0.0, 0.0, 100, 100)
    """
    (ax, ay), (bx, by), (cx, cy) = calcQuadraticParameters(pt1, pt2, pt3)
    ax2 = ax*2.0
    ay2 = ay*2.0
    roots = []
    if ax2 != 0:
        roots.append(-bx/ax2)
    if ay2 != 0:
        roots.append(-by/ay2)
    points = [(ax*t*t + bx*t + cx, ay*t*t + by*t + cy) for t in roots if 0 <= t < 1] + [pt1, pt3]
    return calcBounds(points)
Beispiel #9
0
def calcQuadraticBounds(pt1, pt2, pt3):
    """Return the bounding rectangle for a qudratic bezier segment.
    pt1 and pt3 are the "anchor" points, pt2 is the "handle".

        >>> calcQuadraticBounds((0, 0), (50, 100), (100, 0))
        (0, 0, 100, 50.0)
        >>> calcQuadraticBounds((0, 0), (100, 0), (100, 100))
        (0.0, 0.0, 100, 100)
    """
    (ax, ay), (bx, by), (cx, cy) = calcQuadraticParameters(pt1, pt2, pt3)
    ax2 = ax*2.0
    ay2 = ay*2.0
    roots = []
    if ax2 != 0:
        roots.append(-bx/ax2)
    if ay2 != 0:
        roots.append(-by/ay2)
    points = [(ax*t*t + bx*t + cx, ay*t*t + by*t + cy) for t in roots if 0 <= t < 1] + [pt1, pt3]
    return calcBounds(points)
Beispiel #10
0
def calcQuadraticBounds(pt1, pt2, pt3):
    """Return the bounding rectangle for a qudratic bezier segment.
	pt1 and pt3 are the "anchor" points, pt2 is the "handle".

		>>> calcQuadraticBounds((0, 0), (50, 100), (100, 0))
		(0.0, 0.0, 100.0, 50.0)
		>>> calcQuadraticBounds((0, 0), (100, 0), (100, 100))
		(0.0, 0.0, 100.0, 100.0)
	"""
    a, b, c = calcQuadraticParameters(pt1, pt2, pt3)
    # calc first derivative
    ax, ay = a * 2
    bx, by = b
    roots = []
    if ax != 0:
        roots.append(-bx / ax)
    if ay != 0:
        roots.append(-by / ay)
    points = [a * t * t + b * t + c for t in roots if 0 <= t < 1] + [pt1, pt3]
    return calcBounds(points)
Beispiel #11
0
def calcQuadraticBounds(pt1, pt2, pt3):
	"""Return the bounding rectangle for a qudratic bezier segment.
	pt1 and pt3 are the "anchor" points, pt2 is the "handle".

		>>> calcQuadraticBounds((0, 0), (50, 100), (100, 0))
		(0.0, 0.0, 100.0, 50.0)
		>>> calcQuadraticBounds((0, 0), (100, 0), (100, 100))
		(0.0, 0.0, 100.0, 100.0)
	"""
	a, b, c = calcQuadraticParameters(pt1, pt2, pt3)
	# calc first derivative
	ax, ay = a * 2
	bx, by = b
	roots = []
	if ax != 0:
		roots.append(-bx/ax)
	if ay != 0:
		roots.append(-by/ay)
	points = [a*t*t + b*t + c for t in roots if 0 <= t < 1] + [pt1, pt3]
	return calcBounds(points)
Beispiel #12
0
def calcCubicBounds(pt1, pt2, pt3, pt4):
	"""Return the bounding rectangle for a cubic bezier segment.
	pt1 and pt4 are the "anchor" points, pt2 and pt3 are the "handles".

		>>> calcCubicBounds((0, 0), (25, 100), (75, 100), (100, 0))
		(0.0, 0.0, 100.0, 75.0)
		>>> calcCubicBounds((0, 0), (50, 0), (100, 50), (100, 100))
		(0.0, 0.0, 100.0, 100.0)
		>>> calcCubicBounds((50, 0), (0, 100), (100, 100), (50, 0))
		(35.5662432703, 0.0, 64.4337567297, 75.0)
	"""
	a, b, c, d = calcCubicParameters(pt1, pt2, pt3, pt4)
	# calc first derivative
	ax, ay = a * 3.0
	bx, by = b * 2.0
	cx, cy = c
	xRoots = [t for t in solveQuadratic(ax, bx, cx) if 0 <= t < 1]
	yRoots = [t for t in solveQuadratic(ay, by, cy) if 0 <= t < 1]
	roots = xRoots + yRoots
	
	points = [(a*t*t*t + b*t*t + c * t + d) for t in roots] + [pt1, pt4]
	return calcBounds(points)
Beispiel #13
0
def calcCubicBounds(pt1, pt2, pt3, pt4):
	"""Return the bounding rectangle for a cubic bezier segment.
	pt1 and pt4 are the "anchor" points, pt2 and pt3 are the "handles".

		>>> calcCubicBounds((0, 0), (25, 100), (75, 100), (100, 0))
		(0.0, 0.0, 100.0, 75.0)
		>>> calcCubicBounds((0, 0), (50, 0), (100, 50), (100, 100))
		(0.0, 0.0, 100.0, 100.0)
		>>> calcCubicBounds((50, 0), (0, 100), (100, 100), (50, 0))
		(35.5662432703, 0.0, 64.4337567297, 75.0)
	"""
	a, b, c, d = calcCubicParameters(pt1, pt2, pt3, pt4)
	# calc first derivative
	ax, ay = a * 3.0
	bx, by = b * 2.0
	cx, cy = c
	xRoots = [t for t in solveQuadratic(ax, bx, cx) if 0 <= t < 1]
	yRoots = [t for t in solveQuadratic(ay, by, cy) if 0 <= t < 1]
	roots = xRoots + yRoots
	
	points = [(a*t*t*t + b*t*t + c * t + d) for t in roots] + [pt1, pt4]
	return calcBounds(points)
Beispiel #14
0
def calcCubicBounds(pt1, pt2, pt3, pt4):
    """Return the bounding rectangle for a cubic bezier segment.
    pt1 and pt4 are the "anchor" points, pt2 and pt3 are the "handles".

        >>> calcCubicBounds((0, 0), (25, 100), (75, 100), (100, 0))
        (0, 0, 100, 75.0)
        >>> calcCubicBounds((0, 0), (50, 0), (100, 50), (100, 100))
        (0.0, 0.0, 100, 100)
        >>> print("%f %f %f %f" % calcCubicBounds((50, 0), (0, 100), (100, 100), (50, 0)))
        35.566243 0.000000 64.433757 75.000000
    """
    (ax, ay), (bx, by), (cx, cy), (dx, dy) = calcCubicParameters(pt1, pt2, pt3, pt4)
    # calc first derivative
    ax3 = ax * 3.0
    ay3 = ay * 3.0
    bx2 = bx * 2.0
    by2 = by * 2.0
    xRoots = [t for t in solveQuadratic(ax3, bx2, cx) if 0 <= t < 1]
    yRoots = [t for t in solveQuadratic(ay3, by2, cy) if 0 <= t < 1]
    roots = xRoots + yRoots
    
    points = [(ax*t*t*t + bx*t*t + cx * t + dx, ay*t*t*t + by*t*t + cy * t + dy) for t in roots] + [pt1, pt4]
    return calcBounds(points)
Beispiel #15
0
def drawAICBOutlines(data,
                     pen,
                     fitInside=(None, None, None, None),
                     fixedScale=None):
    """
    Draw outline data from an eps.
    Returns True if the data was drawn, False if not.

    data = the EPS data (string)
    pen = a drawing pen
    fitInside = the maximum size that the outline can be drawn into.
        the function will transform the eps outlines to fit within this box.
        if you don't want to transform the data, send it a box of (None, None, None, None).
        it is also possible to transform based on only horizontal OR vertical parameters.
        Simply send a box formatted like: (None, -250, None, 750) to base the transform
        on vertical dimensions or like: (-1000, None, 1000, None) to base the transform
        on horizontal dimensions.
    fixedScale = a set scale factor for transforming the outline. If the resulting scaled outline
        is larger than the fit rect, the outline will be centered on those parameters.
    """
    from fontTools.pens.transformPen import TransformPen
    from fontTools.misc.arrayTools import calcBounds
    data = '\n'.join(data.splitlines())
    ##
    ## get the point data
    # FL follows the EPSF3.0 spec, but AI seems
    # to be using a different spec. AI puts the outline data
    # in layers. we can get around this by iterating over all lines
    # in the data and drawing points as we find them.
    contours = []
    previousOnCurve = None
    for line in data.splitlines():
        movetoMatch = moveto_RE.match(line)
        if movetoMatch:
            contours.append([])
            x = float(movetoMatch.group(1))
            y = float(movetoMatch.group(2))
            contours[-1].append(('move', [(x, y)]))
            previousOnCurve = (x, y)
            continue
        linetoMatch = lineto_RE.match(line)
        if linetoMatch:
            x = float(linetoMatch.group(1))
            y = float(linetoMatch.group(2))
            contours[-1].append(('line', [(x, y)]))
            previousOnCurve = (x, y)
            continue
        startCurveToMatch = startCurveTo_RE.match(line)
        if startCurveToMatch:
            x1 = float(startCurveToMatch.group(1))
            y1 = float(startCurveToMatch.group(2))
            x2 = float(startCurveToMatch.group(3))
            y2 = float(startCurveToMatch.group(4))
            contours[-1].append(
                ('curve', [previousOnCurve, (x1, y1), (x2, y2)]))
            previousOnCurve = (x2, y2)
            continue
        endCurveToMatch = endCurveTo_RE.match(line)
        if endCurveToMatch:
            x1 = float(endCurveToMatch.group(1))
            y1 = float(endCurveToMatch.group(2))
            x2 = float(endCurveToMatch.group(3))
            y2 = float(endCurveToMatch.group(4))
            contours[-1].append(('curve', [(x1, y1), (x2, y2), (x2, y2)]))
            previousOnCurve = (x2, y2)
            continue
        curvetoMatch = curveto_RE.match(line)
        if curvetoMatch:
            x1 = float(curvetoMatch.group(1))
            y1 = float(curvetoMatch.group(2))
            x2 = float(curvetoMatch.group(3))
            y2 = float(curvetoMatch.group(4))
            x3 = float(curvetoMatch.group(5))
            y3 = float(curvetoMatch.group(6))
            contours[-1].append(('curve', [(x1, y1), (x2, y2), (x3, y3)]))
            previousOnCurve = (x3, y3)
            continue
    # no outline data. give up.
    if not contours:
        return False
    ## get the bounding box
    boundingBox = boundingBox_RE.findall(data)
    if boundingBox:
        # rudely assume that there is only one EPS level bounding box
        # (the spec says that it should be that way)
        boundingBox = [
            int(i.split('.')[0])  # FL writes floats in the bounding box
            for i in boundingBox[0].split(' ')
        ]
    # the EPS does not have a bounding box
    # or the EPS has a stated box of (0, 0, 0, 0)
    # (which AI seems to do for open paths!)
    # so, we get the bounds from a points array
    if not boundingBox or boundingBox == [0, 0, 0, 0]:
        points = []
        for contour in contours:
            for tp, pts in contour:
                points.extend(pts)
        boundingBox = calcBounds(points)
    ##
    ## determine if the outlines need to be transformed
    ## and set up the transformation pen.
    transform = _getRectTransform(fitInside, boundingBox, fixedScale)
    transformPen = TransformPen(pen, transform)
    ##
    ## finally, draw the points
    for contour in contours:
        haveClosedPath = False
        if len(contour) > 1:
            # filter out overlapping points at the
            # start and the end of the contour
            start = contour[0]
            end = contour[-1]
            if end[0] == 'line':
                startPoints = start[1]
                endPoints = end[1]
                if start[0] == end[0]:
                    contour = contour[:-1]
                    haveClosedPath = True
        for tp, pts in contour:
            if tp == 'move':
                transformPen.moveTo(pts[0])
            elif tp == 'line':
                transformPen.lineTo(pts[0])
            elif tp == 'curve':
                pt1, pt2, pt3 = pts
                transformPen.curveTo(pt1, pt2, pt3)
        transformPen.closePath()
        # XXX
        #if haveClosedPath:
        #   transformPen.closePath()
        #else:
        #   transformPen.endPath()
    return True
Beispiel #16
0
## loops
c = 10000

print "(loop %s)" % c

print "with numpy:"
print "calcQuadraticParameters\t\t",
n = time.time()
for i in range(c):
    bezierTools.calcQuadraticParameters(pt1, pt2, pt3)
print time.time() - n

print "calcBounds\t\t\t",
n = time.time()
for i in range(c):
    arrayTools.calcBounds(
        [pt1, pt2, pt3, pt1, pt2, pt3, pt1, pt2, pt3, pt1, pt2, pt3])
print time.time() - n

print "pointsInRect\t\t\t",
n = time.time()
for i in range(c):
    arrayTools.pointsInRect(
        [pt1, pt2, pt3, pt1, pt2, pt3, pt1, pt2, pt3, pt1, pt2, pt3, pt4],
        rect)
print time.time() - n

print "calcQuadraticBounds\t\t",
n = time.time()
for i in range(c):
    bezierTools.calcQuadraticBounds(pt1, pt2, pt3)
print time.time() - n
Beispiel #17
0
def drawAICBOutlines(data, pen, fitInside=(None, None, None, None), fixedScale=None):
    """
    Draw outline data from an eps.
    Returns True if the data was drawn, False if not.

    data = the EPS data (string)
    pen = a drawing pen
    fitInside = the maximum size that the outline can be drawn into.
        the function will transform the eps outlines to fit within this box.
        if you don't want to transform the data, send it a box of (None, None, None, None).
        it is also possible to transform based on only horizontal OR vertical parameters.
        Simply send a box formatted like: (None, -250, None, 750) to base the transform
        on vertical dimensions or like: (-1000, None, 1000, None) to base the transform
        on horizontal dimensions.
    fixedScale = a set scale factor for transforming the outline. If the resulting scaled outline
        is larger than the fit rect, the outline will be centered on those parameters.
    """
    from fontTools.pens.transformPen import TransformPen
    from fontTools.misc.arrayTools import calcBounds
    data = '\n'.join(data.splitlines())
    ##
    ## get the point data
    # FL follows the EPSF3.0 spec, but AI seems
    # to be using a different spec. AI puts the outline data
    # in layers. we can get around this by iterating over all lines
    # in the data and drawing points as we find them.
    contours = []
    previousOnCurve = None
    for line in data.splitlines():
        movetoMatch = moveto_RE.match(line)
        if movetoMatch:
            contours.append([])
            x = float(movetoMatch.group(1))
            y = float(movetoMatch.group(2))
            contours[-1].append(('move', [(x, y)]))
            previousOnCurve = (x, y)
            continue
        linetoMatch = lineto_RE.match(line)
        if linetoMatch:
            x = float(linetoMatch.group(1))
            y = float(linetoMatch.group(2))
            contours[-1].append(('line', [(x, y)]))
            previousOnCurve = (x, y)
            continue
        startCurveToMatch = startCurveTo_RE.match(line)
        if startCurveToMatch:
            x1 = float(startCurveToMatch.group(1))
            y1 = float(startCurveToMatch.group(2))
            x2 = float(startCurveToMatch.group(3))
            y2 = float(startCurveToMatch.group(4))
            contours[-1].append(('curve', [previousOnCurve, (x1, y1), (x2, y2)]))
            previousOnCurve = (x2, y2)
            continue
        endCurveToMatch = endCurveTo_RE.match(line)
        if endCurveToMatch:
            x1 = float(endCurveToMatch.group(1))
            y1 = float(endCurveToMatch.group(2))
            x2 = float(endCurveToMatch.group(3))
            y2 = float(endCurveToMatch.group(4))
            contours[-1].append(('curve', [(x1, y1), (x2, y2), (x2, y2)]))
            previousOnCurve = (x2, y2)
            continue
        curvetoMatch = curveto_RE.match(line)
        if curvetoMatch:
            x1 = float(curvetoMatch.group(1))
            y1 = float(curvetoMatch.group(2))
            x2 = float(curvetoMatch.group(3))
            y2 = float(curvetoMatch.group(4))
            x3 = float(curvetoMatch.group(5))
            y3 = float(curvetoMatch.group(6))
            contours[-1].append(('curve', [(x1, y1), (x2, y2), (x3, y3)]))
            previousOnCurve = (x3, y3)
            continue
    # no outline data. give up.
    if not contours:
        return False
    ## get the bounding box
    boundingBox = boundingBox_RE.findall(data)
    if boundingBox:
        # rudely assume that there is only one EPS level bounding box
        # (the spec says that it should be that way)
        boundingBox = [
                int(i.split('.')[0])    # FL writes floats in the bounding box
                for i in boundingBox[0].split(' ')
                ]
    # the EPS does not have a bounding box
    # or the EPS has a stated box of (0, 0, 0, 0)
    # (which AI seems to do for open paths!)
    # so, we get the bounds from a points array
    if not boundingBox or boundingBox == [0, 0, 0, 0]:
        points = []
        for contour in contours:
            for tp, pts in contour:
                points.extend(pts)
        boundingBox = calcBounds(points)
    ##
    ## determine if the outlines need to be transformed
    ## and set up the transformation pen.
    transform = _getRectTransform(fitInside, boundingBox, fixedScale)
    transformPen = TransformPen(pen, transform)
    ##
    ## finally, draw the points
    for contour in contours:
        haveClosedPath = False
        if len(contour) > 1:
            # filter out overlapping points at the
            # start and the end of the contour
            start = contour[0]
            end = contour[-1]
            if end[0] == 'line':
                startPoints = start[1]
                endPoints = end[1]
                if start[0] == end[0]:
                    contour = contour[:-1]
                    haveClosedPath = True
        for tp, pts in contour:
            if tp == 'move':
                transformPen.moveTo(pts[0])
            elif tp == 'line':
                transformPen.lineTo(pts[0])
            elif tp == 'curve':
                pt1, pt2, pt3 = pts
                transformPen.curveTo(pt1, pt2, pt3)
        transformPen.closePath()
        # XXX
        #if haveClosedPath:
        #   transformPen.closePath()
        #else:
        #   transformPen.endPath()
    return True
Beispiel #18
0
 def _get_controlPointBounds(self):
     from fontTools.misc.transform import Transform
     # storage
     glyphRects = {}
     componentReferences = {}
     # scan loaded glyphs
     for glyphName, glyph in self._glyphs.items():
         if glyphName in self._scheduledForDeletion:
             continue
         glyphRect = glyph.controlPointBounds
         if glyphRect:
             glyphRects[glyphName] = glyphRect
     # scan glyphs that have not been loaded
     if self._glyphSet is not None:
         for glyphName, fileName in self._glyphSet.contents.items():
             if glyphName in self._glyphs or glyphName in self._scheduledForDeletion:
                 continue
             glif = self._glyphSet.getGLIF(glyphName)
             points, components = _fetchControlPointBoundsData(glif)
             if points:
                 glyphRects[glyphName] = calcBounds(points)
             for base, transformation in components:
                 xScale, xyScale, yxScale, yScale, xOffset, yOffset = transformation
                 if glyphName not in componentReferences:
                     componentReferences[glyphName] = []
                 componentReferences[glyphName].append(
                     (base, xScale, xyScale, yxScale, yScale, xOffset,
                      yOffset))
     # get the transformed component bounding boxes and update the glyphs
     for glyphName, components in componentReferences.items():
         glyphRect = glyphRects.get(glyphName, (None, None, None, None))
         # XXX this doesn't handle nested components
         for base, xScale, xyScale, yxScale, yScale, xOffset, yOffset in components:
             # base glyph doesn't exist
             if base not in glyphRects:
                 continue
             baseRect = glyphRects[base]
             # base glyph has no points
             if None in baseRect:
                 continue
             # transform the base rect
             transform = Transform(xx=xScale,
                                   xy=xyScale,
                                   yx=yxScale,
                                   yy=yScale,
                                   dx=xOffset,
                                   dy=yOffset)
             xMin, yMin, xMax, yMax = baseRect
             (xMin, yMin), (xMax, yMax) = transform.transformPoints([
                 (xMin, yMin), (xMax, yMax)
             ])
             componentRect = (xMin, yMin, xMax, yMax)
             # update the glyph rect
             if None in glyphRect:
                 glyphRect = componentRect
             else:
                 glyphRect = unionRect(glyphRect, componentRect)
         # store the updated rect
         glyphRects[glyphName] = glyphRect
     # work out the unified rect
     fontRect = None
     for glyphRect in glyphRects.values():
         if fontRect is None:
             fontRect = glyphRect
         elif glyphRect is not None:
             fontRect = unionRect(fontRect, glyphRect)
     # done
     return fontRect
Beispiel #19
0
def test_calcBounds():
    assert calcBounds([]) == (0, 0, 0, 0)
    assert calcBounds(
        [(0, 40), (0, 100), (50, 50), (80, 10)]) == (0, 10, 80, 100)
Beispiel #20
0
	def recalcBounds(self, glyfTable):
		coordinates, endPts, flags = self.getCoordinates(glyfTable)
		if len(coordinates) > 0:
			self.xMin, self.yMin, self.xMax, self.yMax = calcBounds(coordinates)
		else:
			self.xMin, self.yMin, self.xMax, self.yMax = (0, 0, 0, 0)
Beispiel #21
0
c = 10000

print "(loop %s)"%c


print "with numpy:"
print "calcQuadraticParameters\t\t",
n = time.time()
for i in range(c):
    bezierTools.calcQuadraticParameters(pt1, pt2, pt3)
print time.time() - n

print "calcBounds\t\t\t",
n = time.time()
for i in range(c):
    arrayTools.calcBounds([pt1, pt2, pt3, pt1, pt2, pt3, pt1, pt2, pt3, pt1, pt2, pt3])
print time.time() - n

print "pointsInRect\t\t\t",
n = time.time()
for i in range(c):
    arrayTools.pointsInRect([pt1, pt2, pt3, pt1, pt2, pt3, pt1, pt2, pt3, pt1, pt2, pt3, pt4], rect)
print time.time() - n

print "calcQuadraticBounds\t\t",
n = time.time()
for i in range(c):
    bezierTools.calcQuadraticBounds(pt1, pt2, pt3)
print time.time() - n

print "calcCubicBounds\t\t\t",
Beispiel #22
0
def test_calcBounds():
    assert calcBounds([]) == (0, 0, 0, 0)
    assert calcBounds([(0, 40), (0, 100), (50, 50),
                       (80, 10)]) == (0, 10, 80, 100)