def test_calcIntBounds(): assert calcIntBounds([(0.1, 40.1), (0.1, 100.1), (49.9, 49.9), (78.5, 9.5)]) == (0, 10, 79, 100) assert calcIntBounds([(0.1, 40.1), (0.1, 100.1), (49.9, 49.9), (78.5, 9.5)], round=round) == (0, 10, 78, 100)
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)
def test_calcIntBounds(): assert calcIntBounds( [(0.1, 40.1), (0.1, 100.1), (49.9, 49.9), (78.5, 9.5)] ) == (0, 10, 79, 100) assert calcIntBounds( [(0.1, 40.1), (0.1, 100.1), (49.9, 49.9), (78.5, 9.5)], round=round3 ) == (0, 10, 78, 100)
def _encodeBBox(self, glyphID, glyph): assert glyph.numberOfContours != 0, "empty glyph has no bbox" if not glyph.isComposite(): # for simple glyphs, compare the encoded bounding box info with the calculated # values, and if they match omit the bounding box info currentBBox = glyph.xMin, glyph.yMin, glyph.xMax, glyph.yMax calculatedBBox = calcIntBounds(glyph.coordinates) if currentBBox == calculatedBBox: return self.bboxBitmap[glyphID >> 3] |= 0x80 >> (glyphID & 7) self.bboxStream += sstruct.pack(bboxFormat, glyph)
def transformGlyf(font, glyphBBox="", alt255UInt16=False): glyf = font["glyf"] head = font["head"] nContourStream = "" nPointsStream = "" flagStream = "" glyphStream = "" compositeStream = "" bboxStream = "" instructionStream = "" bboxBitmap = [] bboxBitmapStream = "" for i in range(4 * ((len(glyf.keys()) + 31) / 32)): bboxBitmap.append(0) for glyphName in glyf.glyphOrder: glyph = glyf[glyphName] glyphId = glyf.getGlyphID(glyphName) alternate255UInt16 = 0 # nContourStream nContourStream += struct.pack(">h", glyph.numberOfContours) haveInstructions = False if glyph.numberOfContours == 0: if glyphBBox == "empty": bboxBitmap[glyphId >> 3] |= 0x80 >> (glyphId & 7) bboxStream += struct.pack(">hhhh", 0, 0, 0, 0) continue elif glyph.isComposite(): # compositeStream more = True for i in range(len(glyph.components)): if i == len(glyph.components) - 1: haveInstructions = hasattr(glyph, "program") more = False compositeStream += glyph.components[i].compile(more, haveInstructions, glyf) else: # nPointsStream lastPointIndex = 0 for i in range(glyph.numberOfContours): nPoints = glyph.endPtsOfContours[i] - lastPointIndex + (i == 0) data = pack255UInt16(nPoints, alternate=alternate255UInt16) if nPoints == 506 and alt255UInt16: num = [ord(v) for v in data] if alternate255UInt16 == 0: assert num == [254, 0] elif alternate255UInt16 == 1: assert num == [253, 1, 250] else: assert num == [255, 253] alternate255UInt16 += 1 nPointsStream += data lastPointIndex = glyph.endPtsOfContours[i] # flagStream & glyphStream lastX = 0 lastY = 0 lastPointIndex = 0 for i in range(glyph.numberOfContours): for j in range(lastPointIndex, glyph.endPtsOfContours[i] + 1): x, y = glyph.coordinates[j] onCurve = glyph.flags[j] & 0x01 dx = x - lastX dy = y - lastY lastX = x lastY = y flags, data = packTriplet(dx, dy, onCurve) flagStream += flags glyphStream += data lastPointIndex = glyph.endPtsOfContours[i] + 1 haveInstructions = True if haveInstructions: instructions = glyph.program.getBytecode() # instructionLength glyphStream += pack255UInt16(len(instructions), alternate=alt255UInt16) # instructionStream instructionStream += instructions writeBBox = False if glyph.isComposite(): writeBBox = glyphBBox != "nocomposite" else: coords = glyph.getCoordinates(glyf)[0] oldBounds = (glyph.xMin, glyph.yMin, glyph.xMax, glyph.yMax) newBounds = calcIntBounds(coords) writeBBox = oldBounds != newBounds if writeBBox: # bboxBitmap bboxBitmap[glyphId >> 3] |= 0x80 >> (glyphId & 7) # bboxStream bboxStream += struct.pack(">hhhh", glyph.xMin, glyph.yMin, glyph.xMax, glyph.yMax) bboxBitmapStream = "".join([struct.pack(">B", v) for v in bboxBitmap]) header = deepcopy(woffTransformedGlyfHeader) header["numGlyphs"] = len(glyf.keys()) header["indexFormat"] = head.indexToLocFormat header["nContourStreamSize"] = len(nContourStream) header["nPointsStreamSize"] = len(nPointsStream) header["flagStreamSize"] = len(flagStream) header["glyphStreamSize"] = len(glyphStream) header["compositeStreamSize"] = len(compositeStream) header["bboxStreamSize"] = len(bboxStream) + len(bboxBitmapStream) header["instructionStreamSize"] = len(instructionStream) data = sstruct.pack(woffTransformedGlyfHeaderFormat, header) data += nContourStream + nPointsStream + flagStream data += glyphStream + compositeStream data += bboxBitmapStream + bboxStream data += instructionStream return data
def test_calcIntBounds(): assert calcIntBounds( [(0.1, 40.1), (0.1, 100.1), (49.9, 49.9), (79.5, 9.5)] ) == (0, 10, 80, 100)
def transformGlyf(font, glyphBBox="", alt255UInt16=False): glyf = font["glyf"] head = font["head"] nContourStream = "" nPointsStream = "" flagStream = "" glyphStream = "" compositeStream = "" bboxStream = "" instructionStream = "" bboxBitmap = [] bboxBitmapStream = "" for i in range(4 * ((len(glyf.keys()) + 31) / 32)): bboxBitmap.append(0) for glyphName in glyf.glyphOrder: glyph = glyf[glyphName] glyphId = glyf.getGlyphID(glyphName) # nContourStream nContourStream += struct.pack(">h", glyph.numberOfContours) haveInstructions = False if glyph.numberOfContours == 0: if glyphBBox == "empty": bboxBitmap[glyphId >> 3] |= 0x80 >> (glyphId & 7) bboxStream += struct.pack(">hhhh", 0, 0, 0, 0) continue elif glyph.isComposite(): # compositeStream more = True for i in range(len(glyph.components)): if i == len(glyph.components) - 1: haveInstructions = hasattr(glyph, "program") more = False compositeStream += glyph.components[i].compile(more, haveInstructions, glyf) else: # nPointsStream lastPointIndex = 0 for i in range(glyph.numberOfContours): nPointsStream += pack255UInt16(glyph.endPtsOfContours[i] - lastPointIndex + (i == 0), alternate=alt255UInt16) lastPointIndex = glyph.endPtsOfContours[i] # flagStream & glyphStream lastX = 0 lastY = 0 lastPointIndex = 0 for i in range(glyph.numberOfContours): for j in range(lastPointIndex, glyph.endPtsOfContours[i] + 1): x, y = glyph.coordinates[j] onCurve = glyph.flags[j] & 0x01 dx = x - lastX dy = y - lastY lastX = x lastY = y flags, data = packTriplet(dx, dy, onCurve) flagStream += flags glyphStream += data lastPointIndex = glyph.endPtsOfContours[i] + 1 haveInstructions = True if haveInstructions: instructions = glyph.program.getBytecode() # instructionLength glyphStream += pack255UInt16(len(instructions), alternate=alt255UInt16) # instructionStream instructionStream += instructions writeBBox = False if glyph.isComposite(): writeBBox = glyphBBox != "nocomposite" else: coords = glyph.getCoordinates(glyf)[0] oldBounds = (glyph.xMin, glyph.yMin, glyph.xMax, glyph.yMax) newBounds = calcIntBounds(coords) writeBBox = oldBounds != newBounds if writeBBox: # bboxBitmap bboxBitmap[glyphId >> 3] |= 0x80 >> (glyphId & 7) # bboxStream bboxStream += struct.pack(">hhhh", glyph.xMin, glyph.yMin, glyph.xMax, glyph.yMax) bboxBitmapStream = "".join([struct.pack(">B", v) for v in bboxBitmap]) header = deepcopy(woffTransformedGlyfHeader) header["numGlyphs"] = len(glyf.keys()) header["indexFormat"] = head.indexToLocFormat header["nContourStreamSize"] = len(nContourStream) header["nPointsStreamSize"] = len(nPointsStream) header["flagStreamSize"] = len(flagStream) header["glyphStreamSize"] = len(glyphStream) header["compositeStreamSize"] = len(compositeStream) header["bboxStreamSize"] = len(bboxStream) + len(bboxBitmapStream) header["instructionStreamSize"] = len(instructionStream) data = sstruct.pack(woffTransformedGlyfHeaderFormat, header) data += nContourStream + nPointsStream + flagStream data += glyphStream + compositeStream data += bboxBitmapStream + bboxStream data += instructionStream return data
def recalcBounds(self, glyfTable): coords, endPts, flags = self.getCoordinates(glyfTable) self.xMin, self.yMin, self.xMax, self.yMax = calcIntBounds(coords)