Package fabmetheus_utilities :: Module svg_reader
[hide private]
[frames] | no frames]

Source Code for Module fabmetheus_utilities.svg_reader

  1  """ 
  2  Svg reader. 
  3   
  4  """ 
  5   
  6  from fabmetheus_utilities.geometry.solids import triangle_mesh 
  7  from fabmetheus_utilities.xml_simple_reader import XMLSimpleReader 
  8  from fabmetheus_utilities import archive 
  9  from fabmetheus_utilities import euclidean 
 10  from fabmetheus_utilities import intercircle 
 11  from fabmetheus_utilities import svg_writer 
 12  import math 
 13  import os 
 14  import sys 
 15  import traceback 
 16   
 17   
 18  __author__ = 'Enrique Perez (perez_enrique@yahoo.com)' 
 19  __credits__ = 'Nophead <http://hydraraptor.blogspot.com/>\nArt of Illusion <http://www.artofillusion.org/>' 
 20  __date__ = '$Date: 2008/21/04 $' 
 21  __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' 
 22   
 23   
 24  globalNumberOfCornerPoints = 11 
 25  globalNumberOfBezierPoints = globalNumberOfCornerPoints + globalNumberOfCornerPoints 
 26  globalNumberOfCirclePoints = 4 * globalNumberOfCornerPoints 
 27   
 28   
29 -def addFunctionsToDictionary( dictionary, functions, prefix ):
30 "Add functions to dictionary." 31 for function in functions: 32 dictionary[ function.__name__[ len( prefix ) : ] ] = function
33
34 -def getArcComplexes(begin, end, largeArcFlag, radius, sweepFlag, xAxisRotation):
35 'Get the arc complexes, procedure at http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes' 36 if begin == end: 37 print('Warning, begin equals end in getArcComplexes in svgReader') 38 print(begin) 39 print(end) 40 return [] 41 if radius.imag < 0.0: 42 print('Warning, radius.imag is less than zero in getArcComplexes in svgReader') 43 print(radius) 44 radius = complex(radius.real, abs(radius.imag)) 45 if radius.real < 0.0: 46 print('Warning, radius.real is less than zero in getArcComplexes in svgReader') 47 print(radius) 48 radius = complex(abs(radius.real), radius.imag) 49 if radius.imag <= 0.0: 50 print('Warning, radius.imag is too small for getArcComplexes in svgReader') 51 print(radius) 52 return [end] 53 if radius.real <= 0.0: 54 print('Warning, radius.real is too small for getArcComplexes in svgReader') 55 print(radius) 56 return [end] 57 xAxisRotationComplex = euclidean.getWiddershinsUnitPolar(xAxisRotation) 58 reverseXAxisRotationComplex = complex(xAxisRotationComplex.real, -xAxisRotationComplex.imag) 59 beginRotated = begin * reverseXAxisRotationComplex 60 endRotated = end * reverseXAxisRotationComplex 61 beginTransformed = complex(beginRotated.real / radius.real, beginRotated.imag / radius.imag) 62 endTransformed = complex(endRotated.real / radius.real, endRotated.imag / radius.imag) 63 midpointTransformed = 0.5 * (beginTransformed + endTransformed) 64 midMinusBeginTransformed = midpointTransformed - beginTransformed 65 midMinusBeginTransformedLength = abs(midMinusBeginTransformed) 66 if midMinusBeginTransformedLength > 1.0: 67 print('Warning, midMinusBeginTransformedLength is too large for getArcComplexes in svgReader') 68 print(begin) 69 print(end) 70 print(beginTransformed) 71 print(endTransformed) 72 print(midpointTransformed) 73 print(midMinusBeginTransformed) 74 print('The ellipse will be scaled to fit.') 75 radius *= midMinusBeginTransformedLength 76 scale = 1.0 / midMinusBeginTransformedLength 77 beginTransformed *= scale 78 endTransformed *= scale 79 midpointTransformed *= scale 80 midMinusBeginTransformed *= scale 81 midMinusBeginTransformedLength = 1.0 82 midWiddershinsTransformed = complex(-midMinusBeginTransformed.imag, midMinusBeginTransformed.real) 83 midWiddershinsLengthSquared = 1.0 - midMinusBeginTransformedLength * midMinusBeginTransformedLength 84 if midWiddershinsLengthSquared < 0.0: 85 midWiddershinsLengthSquared = 0.0 86 midWiddershinsLength = math.sqrt(midWiddershinsLengthSquared) 87 midWiddershinsTransformed *= midWiddershinsLength / abs(midWiddershinsTransformed) 88 centerTransformed = midpointTransformed 89 if largeArcFlag == sweepFlag: 90 centerTransformed -= midWiddershinsTransformed 91 else: 92 centerTransformed += midWiddershinsTransformed 93 beginMinusCenterTransformed = beginTransformed - centerTransformed 94 beginMinusCenterTransformedLength = abs(beginMinusCenterTransformed) 95 if beginMinusCenterTransformedLength <= 0.0: 96 return end 97 beginAngle = math.atan2(beginMinusCenterTransformed.imag, beginMinusCenterTransformed.real) 98 endMinusCenterTransformed = endTransformed - centerTransformed 99 angleDifference = euclidean.getAngleDifferenceByComplex(endMinusCenterTransformed, beginMinusCenterTransformed) 100 if sweepFlag: 101 if angleDifference < 0.0: 102 angleDifference += 2.0 * math.pi 103 else: 104 if angleDifference > 0.0: 105 angleDifference -= 2.0 * math.pi 106 global globalSideAngle 107 sides = int(math.ceil(abs(angleDifference) / globalSideAngle)) 108 sideAngle = angleDifference / float(sides) 109 arcComplexes = [] 110 center = complex(centerTransformed.real * radius.real, centerTransformed.imag * radius.imag) * xAxisRotationComplex 111 for side in xrange(1, sides): 112 unitPolar = euclidean.getWiddershinsUnitPolar(beginAngle + float(side) * sideAngle) 113 circumferential = complex(unitPolar.real * radius.real, unitPolar.imag * radius.imag) * beginMinusCenterTransformedLength 114 point = center + circumferential * xAxisRotationComplex 115 arcComplexes.append(point) 116 arcComplexes.append(end) 117 return arcComplexes
118
119 -def getChainMatrixSVG(matrixSVG, xmlElement):
120 "Get chain matrixSVG by svgElement." 121 matrixSVG = matrixSVG.getOtherTimesSelf(getMatrixSVG(xmlElement).tricomplex) 122 if xmlElement.parentNode != None: 123 matrixSVG = getChainMatrixSVG(matrixSVG, xmlElement.parentNode) 124 return matrixSVG
125
126 -def getChainMatrixSVGIfNecessary(xmlElement, yAxisPointingUpward):
127 "Get chain matrixSVG by svgElement and yAxisPointingUpward." 128 matrixSVG = MatrixSVG() 129 if yAxisPointingUpward: 130 return matrixSVG 131 return getChainMatrixSVG(matrixSVG, xmlElement)
132
133 -def getCubicPoint( along, begin, controlPoints, end ):
134 'Get the cubic point.' 135 segmentBegin = getQuadraticPoint( along, begin, controlPoints[0], controlPoints[1] ) 136 segmentEnd = getQuadraticPoint( along, controlPoints[0], controlPoints[1], end ) 137 return ( 1.0 - along ) * segmentBegin + along * segmentEnd
138
139 -def getCubicPoints( begin, controlPoints, end, numberOfBezierPoints=globalNumberOfBezierPoints):
140 'Get the cubic points.' 141 bezierPortion = 1.0 / float(numberOfBezierPoints) 142 cubicPoints = [] 143 for bezierIndex in xrange( 1, numberOfBezierPoints + 1 ): 144 cubicPoints.append(getCubicPoint(bezierPortion * bezierIndex, begin, controlPoints, end)) 145 return cubicPoints
146
147 -def getFontReader(fontFamily):
148 "Get the font reader for the fontFamily." 149 fontLower = fontFamily.lower().replace(' ', '_') 150 global globalFontReaderDictionary 151 if fontLower in globalFontReaderDictionary: 152 return globalFontReaderDictionary[fontLower] 153 global globalFontFileNames 154 if globalFontFileNames == None: 155 globalFontFileNames = archive.getFileNamesByFilePaths(archive.getFilePathsByDirectory(getFontsDirectoryPath())) 156 if fontLower not in globalFontFileNames: 157 print('Warning, the %s font was not found in the fonts folder, so Gentium Basic Regular will be substituted.' % fontFamily) 158 fontLower = 'gentium_basic_regular' 159 fontReader = FontReader(fontLower) 160 globalFontReaderDictionary[fontLower] = fontReader 161 return fontReader
162
163 -def getFontsDirectoryPath():
164 "Get the fonts directory path." 165 return archive.getFabmetheusUtilitiesPath('fonts')
166
167 -def getLabelString(dictionary):
168 "Get the label string for the dictionary." 169 for key in dictionary: 170 labelIndex = key.find('label') 171 if labelIndex >= 0: 172 return dictionary[key] 173 return ''
174
175 -def getMatrixSVG(xmlElement):
176 "Get matrixSVG by svgElement." 177 matrixSVG = MatrixSVG() 178 if 'transform' not in xmlElement.attributeDictionary: 179 return matrixSVG 180 transformWords = [] 181 for transformWord in xmlElement.attributeDictionary['transform'].replace(')', '(').split('('): 182 transformWordStrip = transformWord.strip() 183 if transformWordStrip != '': # workaround for split(character) bug which leaves an extra empty element 184 transformWords.append(transformWordStrip) 185 global globalGetTricomplexDictionary 186 getTricomplexDictionaryKeys = globalGetTricomplexDictionary.keys() 187 for transformWordIndex, transformWord in enumerate(transformWords): 188 if transformWord in getTricomplexDictionaryKeys: 189 transformString = transformWords[transformWordIndex + 1].replace(',', ' ') 190 matrixSVG = matrixSVG.getSelfTimesOther(globalGetTricomplexDictionary[ transformWord ](transformString.split())) 191 return matrixSVG
192
193 -def getQuadraticPoint( along, begin, controlPoint, end ):
194 'Get the quadratic point.' 195 oneMinusAlong = 1.0 - along 196 segmentBegin = oneMinusAlong * begin + along * controlPoint 197 segmentEnd = oneMinusAlong * controlPoint + along * end 198 return oneMinusAlong * segmentBegin + along * segmentEnd
199
200 -def getQuadraticPoints(begin, controlPoint, end, numberOfBezierPoints=globalNumberOfBezierPoints):
201 'Get the quadratic points.' 202 bezierPortion = 1.0 / float(numberOfBezierPoints) 203 quadraticPoints = [] 204 for bezierIndex in xrange(1, numberOfBezierPoints + 1): 205 quadraticPoints.append(getQuadraticPoint(bezierPortion * bezierIndex, begin, controlPoint, end)) 206 return quadraticPoints
207
208 -def getRightStripAlphabetPercent(word):
209 "Get word with alphabet characters and the percent sign stripped from the right." 210 word = word.strip() 211 for characterIndex in xrange(len(word) - 1, -1, -1): 212 character = word[characterIndex] 213 if not character.isalpha() and not character == '%': 214 return float(word[: characterIndex + 1]) 215 return None
216
217 -def getRightStripMinusSplit(lineString):
218 "Get string with spaces after the minus sign stripped." 219 oldLineStringLength = -1 220 while oldLineStringLength < len(lineString): 221 oldLineStringLength = len(lineString) 222 lineString = lineString.replace('- ', '-') 223 return lineString.split()
224
225 -def getStrokeRadius(xmlElement):
226 "Get the stroke radius." 227 return 0.5 * getRightStripAlphabetPercent(getStyleValue('1.0', 'stroke-width', xmlElement))
228
229 -def getStyleValue(defaultValue, key, xmlElement):
230 "Get the stroke value string." 231 if 'style' in xmlElement.attributeDictionary: 232 line = xmlElement.attributeDictionary['style'] 233 strokeIndex = line.find(key) 234 if strokeIndex > -1: 235 words = line[strokeIndex :].replace(':', ' ').replace(';', ' ').split() 236 if len(words) > 1: 237 return words[1] 238 if key in xmlElement.attributeDictionary: 239 return xmlElement.attributeDictionary[key] 240 if xmlElement.parentNode == None: 241 return defaultValue 242 return getStyleValue(defaultValue, key, xmlElement.parentNode)
243
244 -def getTextComplexLoops(fontFamily, fontSize, text, yAxisPointingUpward=True):
245 "Get text as complex loops." 246 textComplexLoops = [] 247 fontReader = getFontReader(fontFamily) 248 horizontalAdvanceX = 0.0 249 for character in text: 250 glyph = fontReader.getGlyph(character, yAxisPointingUpward) 251 textComplexLoops += glyph.getSizedAdvancedLoops(fontSize, horizontalAdvanceX, yAxisPointingUpward) 252 horizontalAdvanceX += glyph.horizontalAdvanceX 253 return textComplexLoops
254
255 -def getTransformedFillOutline(loop, xmlElement, yAxisPointingUpward):
256 "Get the loops if fill is on, otherwise get the outlines." 257 fillOutlineLoops = None 258 if getStyleValue('none', 'fill', xmlElement).lower() == 'none': 259 fillOutlineLoops = intercircle.getAroundsFromLoop(loop, getStrokeRadius(xmlElement)) 260 else: 261 fillOutlineLoops = [loop] 262 return getChainMatrixSVGIfNecessary(xmlElement, yAxisPointingUpward).getTransformedPaths(fillOutlineLoops)
263
264 -def getTransformedOutlineByPath(path, xmlElement, yAxisPointingUpward):
265 "Get the outline from the path." 266 aroundsFromPath = intercircle.getAroundsFromPath(path, getStrokeRadius(xmlElement)) 267 return getChainMatrixSVGIfNecessary(xmlElement, yAxisPointingUpward).getTransformedPaths(aroundsFromPath)
268
269 -def getTransformedOutlineByPaths(paths, xmlElement, yAxisPointingUpward):
270 "Get the outline from the paths." 271 aroundsFromPaths = intercircle.getAroundsFromPaths(paths, getStrokeRadius(xmlElement)) 272 return getChainMatrixSVGIfNecessary(xmlElement, yAxisPointingUpward).getTransformedPaths(aroundsFromPaths)
273
274 -def getTricomplexmatrix(transformWords):
275 "Get matrixSVG by transformWords." 276 tricomplex = [euclidean.getComplexByWords(transformWords)] 277 tricomplex.append(euclidean.getComplexByWords(transformWords, 2)) 278 tricomplex.append(euclidean.getComplexByWords(transformWords, 4)) 279 return tricomplex
280
281 -def getTricomplexrotate(transformWords):
282 "Get matrixSVG by transformWords." 283 rotate = euclidean.getWiddershinsUnitPolar(math.radians(float(transformWords[0]))) 284 return [rotate, complex(-rotate.imag,rotate.real), complex()]
285
286 -def getTricomplexscale(transformWords):
287 "Get matrixSVG by transformWords." 288 scale = euclidean.getComplexByWords(transformWords) 289 return [complex(scale.real,0.0), complex(0.0,scale.imag), complex()]
290
291 -def getTricomplexskewX(transformWords):
292 "Get matrixSVG by transformWords." 293 skewX = math.tan(math.radians(float(transformWords[0]))) 294 return [complex(1.0, 0.0), complex(skewX, 1.0), complex()]
295
296 -def getTricomplexskewY(transformWords):
297 "Get matrixSVG by transformWords." 298 skewY = math.tan(math.radians(float(transformWords[0]))) 299 return [complex(1.0, skewY), complex(0.0, 1.0), complex()]
300
301 -def getTricomplexTimesColumn(firstTricomplex, otherColumn):
302 "Get this matrix multiplied by the otherColumn." 303 dotProductX = firstTricomplex[0].real * otherColumn.real + firstTricomplex[1].real * otherColumn.imag 304 dotProductY = firstTricomplex[0].imag * otherColumn.real + firstTricomplex[1].imag * otherColumn.imag 305 return complex(dotProductX, dotProductY)
306
307 -def getTricomplexTimesOther(firstTricomplex, otherTricomplex):
308 "Get the first tricomplex multiplied by the other tricomplex." 309 #A down, B right from http://en.wikipedia.org/wiki/Matrix_multiplication 310 tricomplexTimesOther = [getTricomplexTimesColumn(firstTricomplex, otherTricomplex[0])] 311 tricomplexTimesOther.append(getTricomplexTimesColumn(firstTricomplex, otherTricomplex[1])) 312 tricomplexTimesOther.append(getTricomplexTimesColumn(firstTricomplex, otherTricomplex[2]) + firstTricomplex[2]) 313 return tricomplexTimesOther
314
315 -def getTricomplextranslate(transformWords):
316 "Get matrixSVG by transformWords." 317 translate = euclidean.getComplexByWords(transformWords) 318 return [complex(1.0, 0.0), complex(0.0, 1.0), translate]
319
320 -def processSVGElementcircle( svgReader, xmlElement ):
321 "Process xmlElement by svgReader." 322 attributeDictionary = xmlElement.attributeDictionary 323 center = euclidean.getComplexDefaultByDictionaryKeys( complex(), attributeDictionary, 'cx', 'cy') 324 radius = euclidean.getFloatDefaultByDictionary( 0.0, attributeDictionary, 'r') 325 if radius == 0.0: 326 print('Warning, in processSVGElementcircle in svgReader radius is zero in:') 327 print(attributeDictionary) 328 return 329 global globalNumberOfCirclePoints 330 global globalSideAngle 331 loop = [] 332 rotatedLoopLayer = svgReader.getRotatedLoopLayer() 333 for side in xrange( globalNumberOfCirclePoints ): 334 unitPolar = euclidean.getWiddershinsUnitPolar( float(side) * globalSideAngle ) 335 loop.append( center + radius * unitPolar ) 336 rotatedLoopLayer.loops += getTransformedFillOutline(loop, xmlElement, svgReader.yAxisPointingUpward)
337
338 -def processSVGElementellipse( svgReader, xmlElement ):
339 "Process xmlElement by svgReader." 340 attributeDictionary = xmlElement.attributeDictionary 341 center = euclidean.getComplexDefaultByDictionaryKeys( complex(), attributeDictionary, 'cx', 'cy') 342 radius = euclidean.getComplexDefaultByDictionaryKeys( complex(), attributeDictionary, 'rx', 'ry') 343 if radius.real == 0.0 or radius.imag == 0.0: 344 print('Warning, in processSVGElementellipse in svgReader radius is zero in:') 345 print(attributeDictionary) 346 return 347 global globalNumberOfCirclePoints 348 global globalSideAngle 349 loop = [] 350 rotatedLoopLayer = svgReader.getRotatedLoopLayer() 351 for side in xrange( globalNumberOfCirclePoints ): 352 unitPolar = euclidean.getWiddershinsUnitPolar( float(side) * globalSideAngle ) 353 loop.append( center + complex( unitPolar.real * radius.real, unitPolar.imag * radius.imag ) ) 354 rotatedLoopLayer.loops += getTransformedFillOutline(loop, xmlElement, svgReader.yAxisPointingUpward)
355
356 -def processSVGElementg(svgReader, xmlElement):
357 'Process xmlElement by svgReader.' 358 if 'id' not in xmlElement.attributeDictionary: 359 return 360 idString = xmlElement.attributeDictionary['id'] 361 if 'beginningOfControlSection' in xmlElement.attributeDictionary: 362 if xmlElement.attributeDictionary['beginningOfControlSection'].lower()[: 1] == 't': 363 svgReader.stopProcessing = True 364 return 365 idStringLower = idString.lower() 366 zIndex = idStringLower.find('z:') 367 if zIndex < 0: 368 idStringLower = getLabelString(xmlElement.attributeDictionary) 369 zIndex = idStringLower.find('z:') 370 if zIndex < 0: 371 return 372 floatFromValue = euclidean.getFloatFromValue(idStringLower[zIndex + len('z:') :].strip()) 373 if floatFromValue != None: 374 svgReader.z = floatFromValue 375 svgReader.bridgeRotation = euclidean.getComplexDefaultByDictionary( None, xmlElement.attributeDictionary, 'bridgeRotation')
376
377 -def processSVGElementline(svgReader, xmlElement):
378 "Process xmlElement by svgReader." 379 begin = euclidean.getComplexDefaultByDictionaryKeys(complex(), xmlElement.attributeDictionary, 'x1', 'y1') 380 end = euclidean.getComplexDefaultByDictionaryKeys(complex(), xmlElement.attributeDictionary, 'x2', 'y2') 381 rotatedLoopLayer = svgReader.getRotatedLoopLayer() 382 rotatedLoopLayer.loops += getTransformedOutlineByPath([begin, end], xmlElement, svgReader.yAxisPointingUpward)
383
384 -def processSVGElementpath( svgReader, xmlElement ):
385 "Process xmlElement by svgReader." 386 if 'd' not in xmlElement.attributeDictionary: 387 print('Warning, in processSVGElementpath in svgReader can not get a value for d in:') 388 print(xmlElement.attributeDictionary) 389 return 390 rotatedLoopLayer = svgReader.getRotatedLoopLayer() 391 PathReader(rotatedLoopLayer.loops, xmlElement, svgReader.yAxisPointingUpward)
392
393 -def processSVGElementpolygon( svgReader, xmlElement ):
394 "Process xmlElement by svgReader." 395 if 'points' not in xmlElement.attributeDictionary: 396 print('Warning, in processSVGElementpolygon in svgReader can not get a value for d in:') 397 print(xmlElement.attributeDictionary) 398 return 399 rotatedLoopLayer = svgReader.getRotatedLoopLayer() 400 words = getRightStripMinusSplit(xmlElement.attributeDictionary['points'].replace(',', ' ')) 401 loop = [] 402 for wordIndex in xrange( 0, len(words), 2 ): 403 loop.append(euclidean.getComplexByWords(words[wordIndex :])) 404 rotatedLoopLayer.loops += getTransformedFillOutline(loop, xmlElement, svgReader.yAxisPointingUpward)
405
406 -def processSVGElementpolyline(svgReader, xmlElement):
407 "Process xmlElement by svgReader." 408 if 'points' not in xmlElement.attributeDictionary: 409 print('Warning, in processSVGElementpolyline in svgReader can not get a value for d in:') 410 print(xmlElement.attributeDictionary) 411 return 412 rotatedLoopLayer = svgReader.getRotatedLoopLayer() 413 words = getRightStripMinusSplit(xmlElement.attributeDictionary['points'].replace(',', ' ')) 414 path = [] 415 for wordIndex in xrange(0, len(words), 2): 416 path.append(euclidean.getComplexByWords(words[wordIndex :])) 417 rotatedLoopLayer.loops += getTransformedOutlineByPath(path, xmlElement, svgReader.yAxisPointingUpward)
418
419 -def processSVGElementrect( svgReader, xmlElement ):
420 "Process xmlElement by svgReader." 421 attributeDictionary = xmlElement.attributeDictionary 422 height = euclidean.getFloatDefaultByDictionary( 0.0, attributeDictionary, 'height') 423 if height == 0.0: 424 print('Warning, in processSVGElementrect in svgReader height is zero in:') 425 print(attributeDictionary) 426 return 427 width = euclidean.getFloatDefaultByDictionary( 0.0, attributeDictionary, 'width') 428 if width == 0.0: 429 print('Warning, in processSVGElementrect in svgReader width is zero in:') 430 print(attributeDictionary) 431 return 432 center = euclidean.getComplexDefaultByDictionaryKeys(complex(), attributeDictionary, 'x', 'y') 433 inradius = 0.5 * complex( width, height ) 434 cornerRadius = euclidean.getComplexDefaultByDictionaryKeys( complex(), attributeDictionary, 'rx', 'ry') 435 rotatedLoopLayer = svgReader.getRotatedLoopLayer() 436 if cornerRadius.real == 0.0 and cornerRadius.imag == 0.0: 437 inradiusMinusX = complex( - inradius.real, inradius.imag ) 438 loop = [center + inradius, center + inradiusMinusX, center - inradius, center - inradiusMinusX] 439 rotatedLoopLayer.loops += getTransformedFillOutline(loop, xmlElement, svgReader.yAxisPointingUpward) 440 return 441 if cornerRadius.real == 0.0: 442 cornerRadius = complex( cornerRadius.imag, cornerRadius.imag ) 443 elif cornerRadius.imag == 0.0: 444 cornerRadius = complex( cornerRadius.real, cornerRadius.real ) 445 cornerRadius = complex( min( cornerRadius.real, inradius.real ), min( cornerRadius.imag, inradius.imag ) ) 446 ellipsePath = [ complex( cornerRadius.real, 0.0 ) ] 447 inradiusMinusCorner = inradius - cornerRadius 448 loop = [] 449 global globalNumberOfCornerPoints 450 global globalSideAngle 451 for side in xrange( 1, globalNumberOfCornerPoints ): 452 unitPolar = euclidean.getWiddershinsUnitPolar( float(side) * globalSideAngle ) 453 ellipsePath.append( complex( unitPolar.real * cornerRadius.real, unitPolar.imag * cornerRadius.imag ) ) 454 ellipsePath.append( complex( 0.0, cornerRadius.imag ) ) 455 cornerPoints = [] 456 for point in ellipsePath: 457 cornerPoints.append( point + inradiusMinusCorner ) 458 cornerPointsReversed = cornerPoints[: : -1] 459 for cornerPoint in cornerPoints: 460 loop.append( center + cornerPoint ) 461 for cornerPoint in cornerPointsReversed: 462 loop.append( center + complex( - cornerPoint.real, cornerPoint.imag ) ) 463 for cornerPoint in cornerPoints: 464 loop.append( center - cornerPoint ) 465 for cornerPoint in cornerPointsReversed: 466 loop.append( center + complex( cornerPoint.real, - cornerPoint.imag ) ) 467 loop = euclidean.getLoopWithoutCloseSequentialPoints( 0.0001 * abs(inradius), loop ) 468 rotatedLoopLayer.loops += getTransformedFillOutline(loop, xmlElement, svgReader.yAxisPointingUpward)
469
470 -def processSVGElementtext(svgReader, xmlElement):
471 "Process xmlElement by svgReader." 472 if svgReader.yAxisPointingUpward: 473 return 474 fontFamily = getStyleValue('Gentium Basic Regular', 'font-family', xmlElement) 475 fontSize = getRightStripAlphabetPercent(getStyleValue('12.0', 'font-size', xmlElement)) 476 matrixSVG = getChainMatrixSVGIfNecessary(xmlElement, svgReader.yAxisPointingUpward) 477 rotatedLoopLayer = svgReader.getRotatedLoopLayer() 478 translate = euclidean.getComplexDefaultByDictionaryKeys(complex(), xmlElement.attributeDictionary, 'x', 'y') 479 for textComplexLoop in getTextComplexLoops(fontFamily, fontSize, xmlElement.text, svgReader.yAxisPointingUpward): 480 translatedLoop = [] 481 for textComplexPoint in textComplexLoop: 482 translatedLoop.append(textComplexPoint + translate ) 483 rotatedLoopLayer.loops.append(matrixSVG.getTransformedPath(translatedLoop))
484 485
486 -class FontReader:
487 "Class to read a font in the fonts folder."
488 - def __init__(self, fontFamily):
489 "Initialize." 490 self.fontFamily = fontFamily 491 self.glyphDictionary = {} 492 self.glyphXMLElementDictionary = {} 493 self.missingGlyph = None 494 fileName = os.path.join(getFontsDirectoryPath(), fontFamily + '.svg') 495 self.xmlParser = XMLSimpleReader(fileName, None, archive.getFileText(fileName)) 496 self.fontXMLElement = self.xmlParser.getRoot().getFirstChildByLocalName('defs').getFirstChildByLocalName('font') 497 self.fontFaceXMLElement = self.fontXMLElement.getFirstChildByLocalName('font-face') 498 self.unitsPerEM = float(self.fontFaceXMLElement.attributeDictionary['units-per-em']) 499 glyphXMLElements = self.fontXMLElement.getChildNodesByLocalName('glyph') 500 for glyphXMLElement in glyphXMLElements: 501 self.glyphXMLElementDictionary[glyphXMLElement.attributeDictionary['unicode']] = glyphXMLElement
502
503 - def getGlyph(self, character, yAxisPointingUpward):
504 "Get the glyph for the character." 505 if character not in self.glyphXMLElementDictionary: 506 if self.missingGlyph == None: 507 missingGlyphXMLElement = self.fontXMLElement.getFirstChildByLocalName('missing-glyph') 508 self.missingGlyph = Glyph(self.unitsPerEM, missingGlyphXMLElement, yAxisPointingUpward) 509 return self.missingGlyph 510 if character not in self.glyphDictionary: 511 self.glyphDictionary[character] = Glyph(self.unitsPerEM, self.glyphXMLElementDictionary[character], yAxisPointingUpward) 512 return self.glyphDictionary[character]
513 514
515 -class Glyph:
516 "Class to handle a glyph."
517 - def __init__(self, unitsPerEM, xmlElement, yAxisPointingUpward):
518 "Initialize." 519 self.horizontalAdvanceX = float(xmlElement.attributeDictionary['horiz-adv-x']) 520 self.loops = [] 521 self.unitsPerEM = unitsPerEM 522 xmlElement.attributeDictionary['fill'] = '' 523 if 'd' not in xmlElement.attributeDictionary: 524 return 525 PathReader(self.loops, xmlElement, yAxisPointingUpward)
526
527 - def getSizedAdvancedLoops(self, fontSize, horizontalAdvanceX, yAxisPointingUpward=True):
528 "Get loops for font size, advanced horizontally." 529 multiplierX = fontSize / self.unitsPerEM 530 multiplierY = multiplierX 531 if not yAxisPointingUpward: 532 multiplierY = -multiplierY 533 sizedLoops = [] 534 for loop in self.loops: 535 sizedLoop = [] 536 sizedLoops.append(sizedLoop) 537 for point in loop: 538 sizedLoop.append( complex(multiplierX * (point.real + horizontalAdvanceX), multiplierY * point.imag)) 539 return sizedLoops
540 541
542 -class MatrixSVG:
543 "Two by three svg matrix."
544 - def __init__(self, tricomplex=None):
545 "Initialize." 546 self.tricomplex = tricomplex
547
548 - def __repr__(self):
549 "Get the string representation of this two by three svg matrix." 550 return str(self.tricomplex)
551
552 - def getOtherTimesSelf(self, otherTricomplex):
553 "Get the other matrix multiplied by this matrix." 554 if otherTricomplex == None: 555 return MatrixSVG(self.tricomplex) 556 if self.tricomplex == None: 557 return MatrixSVG(otherTricomplex) 558 return MatrixSVG(getTricomplexTimesOther(otherTricomplex, self.tricomplex))
559
560 - def getSelfTimesOther(self, otherTricomplex):
561 "Get this matrix multiplied by the other matrix." 562 if otherTricomplex == None: 563 return MatrixSVG(self.tricomplex) 564 if self.tricomplex == None: 565 return MatrixSVG(otherTricomplex) 566 return MatrixSVG(getTricomplexTimesOther(self.tricomplex, otherTricomplex))
567
568 - def getTransformedPath(self, path):
569 "Get transformed path." 570 if self.tricomplex == None: 571 return path 572 complexX = self.tricomplex[0] 573 complexY = self.tricomplex[1] 574 complexTranslation = self.tricomplex[2] 575 transformedPath = [] 576 for point in path: 577 x = complexX.real * point.real + complexY.real * point.imag 578 y = complexX.imag * point.real + complexY.imag * point.imag 579 transformedPath.append(complex(x, y) + complexTranslation) 580 return transformedPath
581
582 - def getTransformedPaths(self, paths):
583 "Get transformed paths." 584 if self.tricomplex == None: 585 return paths 586 transformedPaths = [] 587 for path in paths: 588 transformedPaths.append(self.getTransformedPath(path)) 589 return transformedPaths
590 591
592 -class PathReader:
593 "Class to read svg path."
594 - def __init__(self, loops, xmlElement, yAxisPointingUpward):
595 "Add to path string to loops." 596 self.controlPoints = None 597 self.loops = loops 598 self.oldPoint = None 599 self.outlinePaths = [] 600 self.path = [] 601 self.xmlElement = xmlElement 602 self.yAxisPointingUpward = yAxisPointingUpward 603 pathString = xmlElement.attributeDictionary['d'].replace(',', ' ') 604 global globalProcessPathWordDictionary 605 processPathWordDictionaryKeys = globalProcessPathWordDictionary.keys() 606 for processPathWordDictionaryKey in processPathWordDictionaryKeys: 607 pathString = pathString.replace( processPathWordDictionaryKey, ' %s ' % processPathWordDictionaryKey ) 608 self.words = getRightStripMinusSplit(pathString) 609 for self.wordIndex in xrange( len( self.words ) ): 610 word = self.words[ self.wordIndex ] 611 if word in processPathWordDictionaryKeys: 612 globalProcessPathWordDictionary[word](self) 613 if len(self.path) > 0: 614 self.outlinePaths.append(self.path) 615 self.loops += getTransformedOutlineByPaths(self.outlinePaths, xmlElement, yAxisPointingUpward)
616
617 - def addPathArc( self, end ):
618 "Add an arc to the path." 619 begin = self.getOldPoint() 620 self.controlPoints = None 621 radius = self.getComplexByExtraIndex(1) 622 xAxisRotation = math.radians(float(self.words[self.wordIndex + 3])) 623 largeArcFlag = euclidean.getBooleanFromValue(self.words[ self.wordIndex + 4 ]) 624 sweepFlag = euclidean.getBooleanFromValue(self.words[ self.wordIndex + 5 ]) 625 self.path += getArcComplexes(begin, end, largeArcFlag, radius, sweepFlag, xAxisRotation) 626 self.wordIndex += 8
627
628 - def addPathCubic( self, controlPoints, end ):
629 "Add a cubic curve to the path." 630 begin = self.getOldPoint() 631 self.controlPoints = controlPoints 632 self.path += getCubicPoints( begin, controlPoints, end ) 633 self.wordIndex += 7
634
635 - def addPathCubicReflected( self, controlPoint, end ):
636 "Add a cubic curve to the path from a reflected control point." 637 begin = self.getOldPoint() 638 controlPointBegin = begin 639 if self.controlPoints != None: 640 if len(self.controlPoints) == 2: 641 controlPointBegin = begin + begin - self.controlPoints[-1] 642 self.controlPoints = [controlPointBegin, controlPoint] 643 self.path += getCubicPoints(begin, self.controlPoints, end) 644 self.wordIndex += 5
645
646 - def addPathLine(self, lineFunction, point):
647 "Add a line to the path." 648 self.controlPoints = None 649 self.path.append(point) 650 self.wordIndex += 3 651 self.addPathLineByFunction(lineFunction)
652
653 - def addPathLineAxis(self, point):
654 "Add an axis line to the path." 655 self.controlPoints = None 656 self.path.append(point) 657 self.wordIndex += 2
658
659 - def addPathLineByFunction( self, lineFunction ):
660 "Add a line to the path by line function." 661 while 1: 662 if self.getFloatByExtraIndex() == None: 663 return 664 self.path.append(lineFunction()) 665 self.wordIndex += 2
666
667 - def addPathMove( self, lineFunction, point ):
668 "Add an axis line to the path." 669 self.controlPoints = None 670 if len(self.path) > 0: 671 self.outlinePaths.append(self.path) 672 self.oldPoint = self.path[-1] 673 self.path = [point] 674 self.wordIndex += 3 675 self.addPathLineByFunction(lineFunction)
676
677 - def addPathQuadratic( self, controlPoint, end ):
678 "Add a quadratic curve to the path." 679 begin = self.getOldPoint() 680 self.controlPoints = [controlPoint] 681 self.path += getQuadraticPoints(begin, controlPoint, end) 682 self.wordIndex += 5
683
684 - def addPathQuadraticReflected( self, end ):
685 "Add a quadratic curve to the path from a reflected control point." 686 begin = self.getOldPoint() 687 controlPoint = begin 688 if self.controlPoints != None: 689 if len( self.controlPoints ) == 1: 690 controlPoint = begin + begin - self.controlPoints[-1] 691 self.controlPoints = [ controlPoint ] 692 self.path += getQuadraticPoints(begin, controlPoint, end) 693 self.wordIndex += 3
694
695 - def getComplexByExtraIndex( self, extraIndex=0 ):
696 'Get complex from the extraIndex.' 697 return euclidean.getComplexByWords(self.words, self.wordIndex + extraIndex)
698
699 - def getComplexRelative(self):
700 "Get relative complex." 701 return self.getComplexByExtraIndex() + self.getOldPoint()
702
703 - def getFloatByExtraIndex( self, extraIndex=0 ):
704 'Get float from the extraIndex.' 705 totalIndex = self.wordIndex + extraIndex 706 if totalIndex >= len(self.words): 707 return None 708 word = self.words[totalIndex] 709 if word[: 1].isalpha(): 710 return None 711 return euclidean.getFloatFromValue(word)
712
713 - def getOldPoint(self):
714 'Get the old point.' 715 if len(self.path) > 0: 716 return self.path[-1] 717 return self.oldPoint
718
719 - def processPathWordA(self):
720 'Process path word A.' 721 self.addPathArc( self.getComplexByExtraIndex( 6 ) )
722
723 - def processPathWorda(self):
724 'Process path word a.' 725 self.addPathArc(self.getComplexByExtraIndex(6) + self.getOldPoint())
726
727 - def processPathWordC(self):
728 'Process path word C.' 729 end = self.getComplexByExtraIndex( 5 ) 730 self.addPathCubic( [ self.getComplexByExtraIndex( 1 ), self.getComplexByExtraIndex(3) ], end )
731
732 - def processPathWordc(self):
733 'Process path word C.' 734 begin = self.getOldPoint() 735 end = self.getComplexByExtraIndex( 5 ) 736 self.addPathCubic( [ self.getComplexByExtraIndex( 1 ) + begin, self.getComplexByExtraIndex(3) + begin ], end + begin )
737
738 - def processPathWordH(self):
739 "Process path word H." 740 beginY = self.getOldPoint().imag 741 self.addPathLineAxis(complex(float(self.words[self.wordIndex + 1]), beginY)) 742 while 1: 743 floatByExtraIndex = self.getFloatByExtraIndex() 744 if floatByExtraIndex == None: 745 return 746 self.path.append(complex(floatByExtraIndex, beginY)) 747 self.wordIndex += 1
748
749 - def processPathWordh(self):
750 "Process path word h." 751 begin = self.getOldPoint() 752 self.addPathLineAxis(complex(float(self.words[self.wordIndex + 1]) + begin.real, begin.imag)) 753 while 1: 754 floatByExtraIndex = self.getFloatByExtraIndex() 755 if floatByExtraIndex == None: 756 return 757 self.path.append(complex(floatByExtraIndex + self.getOldPoint().real, begin.imag)) 758 self.wordIndex += 1
759
760 - def processPathWordL(self):
761 "Process path word L." 762 self.addPathLine(self.getComplexByExtraIndex, self.getComplexByExtraIndex( 1 ))
763
764 - def processPathWordl(self):
765 "Process path word l." 766 self.addPathLine(self.getComplexRelative, self.getComplexByExtraIndex(1) + self.getOldPoint())
767
768 - def processPathWordM(self):
769 "Process path word M." 770 self.addPathMove(self.getComplexByExtraIndex, self.getComplexByExtraIndex(1))
771
772 - def processPathWordm(self):
773 "Process path word m." 774 self.addPathMove(self.getComplexRelative, self.getComplexByExtraIndex(1) + self.getOldPoint())
775
776 - def processPathWordQ(self):
777 'Process path word Q.' 778 self.addPathQuadratic( self.getComplexByExtraIndex( 1 ), self.getComplexByExtraIndex(3) )
779
780 - def processPathWordq(self):
781 'Process path word q.' 782 begin = self.getOldPoint() 783 self.addPathQuadratic(self.getComplexByExtraIndex(1) + begin, self.getComplexByExtraIndex(3) + begin)
784
785 - def processPathWordS(self):
786 'Process path word S.' 787 self.addPathCubicReflected( self.getComplexByExtraIndex( 1 ), self.getComplexByExtraIndex(3) )
788
789 - def processPathWords(self):
790 'Process path word s.' 791 begin = self.getOldPoint() 792 self.addPathCubicReflected(self.getComplexByExtraIndex(1) + begin, self.getComplexByExtraIndex(3) + begin)
793
794 - def processPathWordT(self):
795 'Process path word T.' 796 self.addPathQuadraticReflected( self.getComplexByExtraIndex( 1 ) )
797
798 - def processPathWordt(self):
799 'Process path word t.' 800 self.addPathQuadraticReflected(self.getComplexByExtraIndex(1) + self.getOldPoint())
801
802 - def processPathWordV(self):
803 "Process path word V." 804 beginX = self.getOldPoint().real 805 self.addPathLineAxis(complex(beginX, float(self.words[self.wordIndex + 1]))) 806 while 1: 807 floatByExtraIndex = self.getFloatByExtraIndex() 808 if floatByExtraIndex == None: 809 return 810 self.path.append(complex(beginX, floatByExtraIndex)) 811 self.wordIndex += 1
812
813 - def processPathWordv(self):
814 "Process path word v." 815 begin = self.getOldPoint() 816 self.addPathLineAxis(complex(begin.real, float(self.words[self.wordIndex + 1]) + begin.imag)) 817 while 1: 818 floatByExtraIndex = self.getFloatByExtraIndex() 819 if floatByExtraIndex == None: 820 return 821 self.path.append(complex(begin.real, floatByExtraIndex + self.getOldPoint().imag)) 822 self.wordIndex += 1
823
824 - def processPathWordZ(self):
825 "Process path word Z." 826 self.controlPoints = None 827 if len(self.path) < 1: 828 return 829 self.loops.append(getChainMatrixSVGIfNecessary(self.xmlElement, self.yAxisPointingUpward).getTransformedPath(self.path)) 830 self.oldPoint = self.path[0] 831 self.path = []
832
833 - def processPathWordz(self):
834 "Process path word z." 835 self.processPathWordZ()
836 837
838 -class SVGReader:
839 "An svg carving."
840 - def __init__(self):
841 "Add empty lists." 842 self.bridgeRotation = None 843 self.rotatedLoopLayers = [] 844 self.sliceDictionary = None 845 self.stopProcessing = False 846 self.z = 0.0
847
848 - def flipDirectLayer(self, rotatedLoopLayer):
849 "Flip the y coordinate of the layer and direct the loops." 850 for loop in rotatedLoopLayer.loops: 851 for pointIndex, point in enumerate(loop): 852 loop[pointIndex] = complex(point.real, -point.imag) 853 triangle_mesh.sortLoopsInOrderOfArea(True, rotatedLoopLayer.loops) 854 for loopIndex, loop in enumerate(rotatedLoopLayer.loops): 855 isInsideLoops = euclidean.getIsInFilledRegion(rotatedLoopLayer.loops[: loopIndex], euclidean.getLeftPoint(loop)) 856 intercircle.directLoop((not isInsideLoops), loop)
857
858 - def getRotatedLoopLayer(self):
859 "Return the rotated loop layer." 860 if self.z != None: 861 rotatedLoopLayer = euclidean.RotatedLoopLayer( self.z ) 862 self.rotatedLoopLayers.append( rotatedLoopLayer ) 863 rotatedLoopLayer.rotation = self.bridgeRotation 864 self.z = None 865 return self.rotatedLoopLayers[-1]
866
867 - def parseSVG(self, fileName, svgText):
868 "Parse SVG text and store the layers." 869 self.fileName = fileName 870 xmlParser = XMLSimpleReader(fileName, None, svgText) 871 self.root = xmlParser.getRoot() 872 if self.root == None: 873 print('Warning, root was None in parseSVG in SVGReader, so nothing will be done for:') 874 print(fileName) 875 return 876 self.parseSVGByXMLElement(self.root)
877
878 - def parseSVGByXMLElement(self, xmlElement):
879 "Parse SVG by xmlElement." 880 self.sliceDictionary = svg_writer.getSliceDictionary(xmlElement) 881 self.yAxisPointingUpward = euclidean.getBooleanFromDictionary(False, self.sliceDictionary, 'yAxisPointingUpward') 882 self.processXMLElement(xmlElement) 883 if not self.yAxisPointingUpward: 884 for rotatedLoopLayer in self.rotatedLoopLayers: 885 self.flipDirectLayer(rotatedLoopLayer)
886
887 - def processXMLElement(self, xmlElement):
888 "Process the xml element." 889 if self.stopProcessing: 890 return 891 lowerLocalName = xmlElement.localName.lower() 892 global globalProcessSVGElementDictionary 893 if lowerLocalName in globalProcessSVGElementDictionary: 894 try: 895 globalProcessSVGElementDictionary[ lowerLocalName ](self, xmlElement) 896 except: 897 print('Warning, in processXMLElement in svg_reader, could not process:') 898 print(xmlElement) 899 traceback.print_exc(file=sys.stdout) 900 for childNode in xmlElement.childNodes: 901 self.processXMLElement( childNode )
902 903 904 globalFontFileNames = None 905 globalFontReaderDictionary = {} 906 globalGetTricomplexDictionary = {} 907 globalGetTricomplexFunctions = [ 908 getTricomplexmatrix, 909 getTricomplexrotate, 910 getTricomplexscale, 911 getTricomplexskewX, 912 getTricomplexskewY, 913 getTricomplextranslate ] 914 globalProcessPathWordFunctions = [ 915 PathReader.processPathWordA, 916 PathReader.processPathWorda, 917 PathReader.processPathWordC, 918 PathReader.processPathWordc, 919 PathReader.processPathWordH, 920 PathReader.processPathWordh, 921 PathReader.processPathWordL, 922 PathReader.processPathWordl, 923 PathReader.processPathWordM, 924 PathReader.processPathWordm, 925 PathReader.processPathWordQ, 926 PathReader.processPathWordq, 927 PathReader.processPathWordS, 928 PathReader.processPathWords, 929 PathReader.processPathWordT, 930 PathReader.processPathWordt, 931 PathReader.processPathWordV, 932 PathReader.processPathWordv, 933 PathReader.processPathWordZ, 934 PathReader.processPathWordz ] 935 globalProcessPathWordDictionary = {} 936 globalProcessSVGElementDictionary = {} 937 globalProcessSVGElementFunctions = [ 938 processSVGElementcircle, 939 processSVGElementellipse, 940 processSVGElementg, 941 processSVGElementline, 942 processSVGElementpath, 943 processSVGElementpolygon, 944 processSVGElementpolyline, 945 processSVGElementrect, 946 processSVGElementtext ] 947 globalSideAngle = 0.5 * math.pi / float( globalNumberOfCornerPoints ) 948 949 950 addFunctionsToDictionary( globalGetTricomplexDictionary, globalGetTricomplexFunctions, 'getTricomplex') 951 addFunctionsToDictionary( globalProcessPathWordDictionary, globalProcessPathWordFunctions, 'processPathWord') 952 addFunctionsToDictionary( globalProcessSVGElementDictionary, globalProcessSVGElementFunctions, 'processSVGElement') 953