| 1 | from AppKit import NSBundle, NSURL |
|---|
| 2 | bundle = NSBundle.mainBundle() |
|---|
| 3 | |
|---|
| 4 | path = bundle.pathForResource_ofType_("webDefaultBackground", "png") |
|---|
| 5 | url = NSURL.fileURLWithPath_(path) |
|---|
| 6 | webDefaultBackgroundImageURL = url.absoluteString() |
|---|
| 7 | |
|---|
| 8 | path = bundle.pathForResource_ofType_("webDisclosureClosed", "png") |
|---|
| 9 | url = NSURL.fileURLWithPath_(path) |
|---|
| 10 | closedDisclosureImageURL = url.absoluteString() |
|---|
| 11 | |
|---|
| 12 | path = bundle.pathForResource_ofType_("webDisclosureOpen", "png") |
|---|
| 13 | url = NSURL.fileURLWithPath_(path) |
|---|
| 14 | openDisclosureImageURL = url.absoluteString() |
|---|
| 15 | |
|---|
| 16 | |
|---|
| 17 | |
|---|
| 18 | defaultHTML = """ |
|---|
| 19 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" |
|---|
| 20 | SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
|---|
| 21 | |
|---|
| 22 | <html xmlns="http://www.w3.org/1999/xhtml"> |
|---|
| 23 | |
|---|
| 24 | <head> |
|---|
| 25 | <meta http-equiv="Content-Type" content="text/html; charset=utf-16" /> |
|---|
| 26 | <style type="text/css"> |
|---|
| 27 | |
|---|
| 28 | body { |
|---|
| 29 | background-color: #3d3d3d; |
|---|
| 30 | background-image: url(%s); |
|---|
| 31 | background-repeat: repeat-x; |
|---|
| 32 | } |
|---|
| 33 | |
|---|
| 34 | </style> |
|---|
| 35 | </head> |
|---|
| 36 | <body> |
|---|
| 37 | </body> |
|---|
| 38 | </html |
|---|
| 39 | """ % (webDefaultBackgroundImageURL) |
|---|
| 40 | |
|---|
| 41 | _head = u""" |
|---|
| 42 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" |
|---|
| 43 | SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
|---|
| 44 | |
|---|
| 45 | <html xmlns="http://www.w3.org/1999/xhtml"> |
|---|
| 46 | |
|---|
| 47 | <head> |
|---|
| 48 | <meta http-equiv="Content-Type" content="text/html; charset=utf-16" /> |
|---|
| 49 | <style type="text/css"> |
|---|
| 50 | |
|---|
| 51 | body { |
|---|
| 52 | margin: 0px; |
|---|
| 53 | padding: 0px; |
|---|
| 54 | background-color: #fff; |
|---|
| 55 | color: black; |
|---|
| 56 | font: 12px/24px "Lucida Grande", "Lucida Sans" Monaco, Andale Mono, monospaced; |
|---|
| 57 | } |
|---|
| 58 | |
|---|
| 59 | /* heads */ |
|---|
| 60 | |
|---|
| 61 | h1, h2, h3, h4, h5, h6 { |
|---|
| 62 | font-size: 12px; |
|---|
| 63 | font-weight: normal; |
|---|
| 64 | line-height: 23px; |
|---|
| 65 | background-color: #738499; |
|---|
| 66 | color: white; |
|---|
| 67 | margin: 0px; |
|---|
| 68 | padding: 0px 10px 0px 20px; |
|---|
| 69 | border-bottom: 1px solid #fff; |
|---|
| 70 | } |
|---|
| 71 | |
|---|
| 72 | h2 { |
|---|
| 73 | background-color: #8da3b3; |
|---|
| 74 | color: white; |
|---|
| 75 | } |
|---|
| 76 | |
|---|
| 77 | h3 { |
|---|
| 78 | background-color: #b4c3d4; |
|---|
| 79 | color: white; |
|---|
| 80 | } |
|---|
| 81 | |
|---|
| 82 | h4 { |
|---|
| 83 | padding-left: 10px; |
|---|
| 84 | background-color: #ced9e9; |
|---|
| 85 | color: black; |
|---|
| 86 | } |
|---|
| 87 | |
|---|
| 88 | h5 { |
|---|
| 89 | padding-left: 10px; |
|---|
| 90 | background-color: #f0f0f0; |
|---|
| 91 | color: black; |
|---|
| 92 | border-bottom: 1px solid #d8d8d8; |
|---|
| 93 | } |
|---|
| 94 | |
|---|
| 95 | h6 { |
|---|
| 96 | padding-left: 10px; |
|---|
| 97 | background-color: #fff; |
|---|
| 98 | color: black; |
|---|
| 99 | border-bottom: 1px solid #d8d8d8; |
|---|
| 100 | } |
|---|
| 101 | |
|---|
| 102 | /* disclosure */ |
|---|
| 103 | |
|---|
| 104 | h1.withDisclosure { |
|---|
| 105 | background-image: url(%s); |
|---|
| 106 | background-repeat: no-repeat; |
|---|
| 107 | } |
|---|
| 108 | |
|---|
| 109 | h2.withDisclosure { |
|---|
| 110 | background-image: url(%s); |
|---|
| 111 | background-repeat: no-repeat; |
|---|
| 112 | } |
|---|
| 113 | |
|---|
| 114 | h3.withDisclosure { |
|---|
| 115 | background-image: url(%s); |
|---|
| 116 | background-repeat: no-repeat; |
|---|
| 117 | } |
|---|
| 118 | |
|---|
| 119 | /* misc */ |
|---|
| 120 | |
|---|
| 121 | p { |
|---|
| 122 | margin: -1px 0px 0px 0px; |
|---|
| 123 | padding: 0px 10px 0px 10px; |
|---|
| 124 | border-top: 1px solid #d8d8d8; |
|---|
| 125 | border-bottom: 1px solid #d8d8d8; |
|---|
| 126 | } |
|---|
| 127 | |
|---|
| 128 | span.valueID { |
|---|
| 129 | color: #8f8f8f; |
|---|
| 130 | } |
|---|
| 131 | |
|---|
| 132 | canvas { |
|---|
| 133 | margin: 0px; |
|---|
| 134 | padding: 0px; |
|---|
| 135 | } |
|---|
| 136 | |
|---|
| 137 | /* ------------ */ |
|---|
| 138 | /* log specific */ |
|---|
| 139 | /* ------------ */ |
|---|
| 140 | |
|---|
| 141 | p.logOutput { |
|---|
| 142 | background-color: #fcc; |
|---|
| 143 | } |
|---|
| 144 | |
|---|
| 145 | span.logCheckOn { |
|---|
| 146 | color: #8f8f8f; |
|---|
| 147 | margin-right: 5px; |
|---|
| 148 | } |
|---|
| 149 | |
|---|
| 150 | span.logCheckOff { |
|---|
| 151 | color: #fff; |
|---|
| 152 | margin-right: 5px; |
|---|
| 153 | } |
|---|
| 154 | |
|---|
| 155 | span.logProcessedSeparator { |
|---|
| 156 | color: #FF0000; |
|---|
| 157 | } |
|---|
| 158 | |
|---|
| 159 | /* ------------------ */ |
|---|
| 160 | /* test case specific */ |
|---|
| 161 | /* ------------------ */ |
|---|
| 162 | |
|---|
| 163 | p.testCaseNote { |
|---|
| 164 | background-color: #f0f0f0; |
|---|
| 165 | } |
|---|
| 166 | |
|---|
| 167 | p.testCaseFeature { |
|---|
| 168 | padding-left: 20px; |
|---|
| 169 | } |
|---|
| 170 | |
|---|
| 171 | p.testCaseInput { |
|---|
| 172 | background-color : #f0f0f0; |
|---|
| 173 | } |
|---|
| 174 | |
|---|
| 175 | p.testCaseResult { |
|---|
| 176 | } |
|---|
| 177 | |
|---|
| 178 | p.testCaseExpectedResultPassed { |
|---|
| 179 | background-color : #cfc; |
|---|
| 180 | } |
|---|
| 181 | |
|---|
| 182 | p.testCaseExpectedResultFailed { |
|---|
| 183 | background-color : #fcc; |
|---|
| 184 | } |
|---|
| 185 | |
|---|
| 186 | .testCaseInputCanvasContainer { |
|---|
| 187 | margin: -1px 0px 10px 0px; |
|---|
| 188 | background-color : #f0f0f0; |
|---|
| 189 | border-top: 1px solid #d8d8d8; |
|---|
| 190 | border-bottom: 1px solid #d8d8d8; |
|---|
| 191 | } |
|---|
| 192 | |
|---|
| 193 | .testCaseResultCanvasContainer { |
|---|
| 194 | margin: -11px 0px 10px 0px; |
|---|
| 195 | background-color: #fff; |
|---|
| 196 | border-top: 1px solid #d8d8d8; |
|---|
| 197 | border-bottom: 1px solid #d8d8d8; |
|---|
| 198 | } |
|---|
| 199 | |
|---|
| 200 | .testCaseExpectedResultPassedCanvasContainer { |
|---|
| 201 | margin: -11px 0px 10px 0px; |
|---|
| 202 | background-color: #cfc; |
|---|
| 203 | border-top: 1px solid #d8d8d8; |
|---|
| 204 | border-bottom: 1px solid #d8d8d8; |
|---|
| 205 | } |
|---|
| 206 | |
|---|
| 207 | .testCaseExpectedResultFailedCanvasContainer { |
|---|
| 208 | margin: -11px 0px 10px 0px; |
|---|
| 209 | background-color: #fcc; |
|---|
| 210 | border-top: 1px solid #d8d8d8; |
|---|
| 211 | border-bottom: 1px solid #d8d8d8; |
|---|
| 212 | } |
|---|
| 213 | |
|---|
| 214 | </style> |
|---|
| 215 | |
|---|
| 216 | <script type="text/javascript"> |
|---|
| 217 | function toggle(elementID) { |
|---|
| 218 | var element = document.getElementById(elementID); |
|---|
| 219 | var head = document.getElementById(elementID + "_head"); |
|---|
| 220 | if (element.style.display == "") { |
|---|
| 221 | element.style.display = "none"; |
|---|
| 222 | head.style.backgroundImage = "url(%s)"; |
|---|
| 223 | } else if (element.style.display != "block") { |
|---|
| 224 | element.style.display = "block"; |
|---|
| 225 | head.style.backgroundImage = "url(%s)"; |
|---|
| 226 | } else { |
|---|
| 227 | element.style.display = "none"; |
|---|
| 228 | head.style.backgroundImage = "url(%s)"; |
|---|
| 229 | } |
|---|
| 230 | } |
|---|
| 231 | </script> |
|---|
| 232 | |
|---|
| 233 | <!-- CANVAS SCRIPT --> |
|---|
| 234 | |
|---|
| 235 | </head> |
|---|
| 236 | <!-- BODY -->""" % (openDisclosureImageURL, openDisclosureImageURL, openDisclosureImageURL, closedDisclosureImageURL, openDisclosureImageURL, closedDisclosureImageURL) |
|---|
| 237 | |
|---|
| 238 | _testCaseCanvasJavaScript = u""" |
|---|
| 239 | <script> |
|---|
| 240 | |
|---|
| 241 | var glyphs = { |
|---|
| 242 | <!-- GLYPH DEFINITIONS --> |
|---|
| 243 | }; |
|---|
| 244 | |
|---|
| 245 | var allCanvasData = [ |
|---|
| 246 | <!-- ALL CANVAS DATA --> |
|---|
| 247 | ]; |
|---|
| 248 | |
|---|
| 249 | var unitsPerEm = <!-- FONT UPM -->; |
|---|
| 250 | var descender = <!-- FONT DESCENDER -->; |
|---|
| 251 | var scaleFactor = <!-- GLYPH POINT SIZE --> / unitsPerEm; |
|---|
| 252 | var inverseScaleFactor = 1.0 / scaleFactor; |
|---|
| 253 | |
|---|
| 254 | function renderGlyphRecords(glyphRecords, canvasID) { |
|---|
| 255 | var canvas = document.getElementById(canvasID); |
|---|
| 256 | var margin = 10 * inverseScaleFactor; |
|---|
| 257 | var canvasWidth = document.body.clientWidth; |
|---|
| 258 | var maxLineWidth = (canvasWidth - 20) * inverseScaleFactor; |
|---|
| 259 | |
|---|
| 260 | // break into lines |
|---|
| 261 | var lines = [[]]; |
|---|
| 262 | var lineWidth = 0; |
|---|
| 263 | for (recordIndex in glyphRecords) { |
|---|
| 264 | var record = glyphRecords[recordIndex]; |
|---|
| 265 | var name = record[0]; |
|---|
| 266 | var xPlacement = record[1]; |
|---|
| 267 | var yPlacement = record[2]; |
|---|
| 268 | var xAdvance = record[3]; |
|---|
| 269 | var yAdvance = record[4]; |
|---|
| 270 | var glyphData = glyphs[name]; |
|---|
| 271 | var width = glyphData[0]; |
|---|
| 272 | |
|---|
| 273 | if (lineWidth + width + xAdvance > maxLineWidth) { |
|---|
| 274 | lineWidth = 0; |
|---|
| 275 | lines[lines.length] = []; |
|---|
| 276 | } |
|---|
| 277 | var line = lines[lines.length - 1]; |
|---|
| 278 | line[line.length] = record; |
|---|
| 279 | lineWidth = lineWidth + width + xAdvance; |
|---|
| 280 | } |
|---|
| 281 | |
|---|
| 282 | // set the canvas size |
|---|
| 283 | var canvasHeight = ((lines.length * unitsPerEm) + (margin * 2)) * scaleFactor; |
|---|
| 284 | canvas.height = canvasHeight; |
|---|
| 285 | canvas.width = canvasWidth; |
|---|
| 286 | |
|---|
| 287 | // get the context |
|---|
| 288 | var context = canvas.getContext("2d"); |
|---|
| 289 | |
|---|
| 290 | // do the flip transformation |
|---|
| 291 | context.scale(1, -1); |
|---|
| 292 | |
|---|
| 293 | // set the scale and shift for the first line |
|---|
| 294 | context.scale(scaleFactor, scaleFactor); |
|---|
| 295 | context.translate(margin, -unitsPerEm-descender-margin); |
|---|
| 296 | |
|---|
| 297 | // draw the lines |
|---|
| 298 | context.fillStyle = "black"; |
|---|
| 299 | for (lineIndex in lines) { |
|---|
| 300 | var line = lines[lineIndex]; |
|---|
| 301 | context.save() |
|---|
| 302 | for (recordIndex in line) { |
|---|
| 303 | var record = line[recordIndex]; |
|---|
| 304 | var name = record[0]; |
|---|
| 305 | var xPlacement = record[1]; |
|---|
| 306 | var yPlacement = record[2]; |
|---|
| 307 | var xAdvance = record[3]; |
|---|
| 308 | var yAdvance = record[4]; |
|---|
| 309 | var glyphData = glyphs[name]; |
|---|
| 310 | var width = glyphData[0]; |
|---|
| 311 | // handle placement |
|---|
| 312 | context.save() |
|---|
| 313 | context.translate(xPlacement, yPlacement); |
|---|
| 314 | // draw the outline |
|---|
| 315 | for (instructionIndex in glyphData) { |
|---|
| 316 | if (instructionIndex > 0) { |
|---|
| 317 | var instruction = glyphData[instructionIndex]; |
|---|
| 318 | var instructionType = instruction[0]; |
|---|
| 319 | if (instructionType == "beginPath") { |
|---|
| 320 | context.beginPath(); |
|---|
| 321 | } else if (instructionType == "moveTo") { |
|---|
| 322 | var x = instruction[1][0]; |
|---|
| 323 | var y = instruction[1][1]; |
|---|
| 324 | context.moveTo(x, y); |
|---|
| 325 | } else if (instructionType == "lineTo") { |
|---|
| 326 | var x = instruction[1][0]; |
|---|
| 327 | var y = instruction[1][1]; |
|---|
| 328 | context.lineTo(x, y); |
|---|
| 329 | } else if (instructionType == "bezierCurveTo") { |
|---|
| 330 | var x1 = instruction[1][0]; |
|---|
| 331 | var y1 = instruction[1][1]; |
|---|
| 332 | var x2 = instruction[1][2]; |
|---|
| 333 | var y2 = instruction[1][3]; |
|---|
| 334 | var x3 = instruction[1][4]; |
|---|
| 335 | var y3 = instruction[1][5]; |
|---|
| 336 | context.bezierCurveTo(x1, y1, x2, y2, x3, y3); |
|---|
| 337 | } else if (instructionType == "closePath") { |
|---|
| 338 | context.closePath(); |
|---|
| 339 | } |
|---|
| 340 | } |
|---|
| 341 | } |
|---|
| 342 | // fill the path |
|---|
| 343 | if (glyphData.length > 1) { |
|---|
| 344 | context.fill(); |
|---|
| 345 | } |
|---|
| 346 | // restore the pre-placement position |
|---|
| 347 | context.restore(); |
|---|
| 348 | // do the advance changes |
|---|
| 349 | context.translate(width + xAdvance, yAdvance); |
|---|
| 350 | } |
|---|
| 351 | context.restore(); |
|---|
| 352 | // shift to the next line |
|---|
| 353 | context.translate(0, -unitsPerEm); |
|---|
| 354 | } |
|---|
| 355 | } |
|---|
| 356 | |
|---|
| 357 | function renderAllCanvases() { |
|---|
| 358 | for (canvasIndex in allCanvasData) { |
|---|
| 359 | var canvasData = allCanvasData[canvasIndex]; |
|---|
| 360 | var canvasID = canvasData[0]; |
|---|
| 361 | var glyphRecords = canvasData[1]; |
|---|
| 362 | renderGlyphRecords(glyphRecords, canvasID); |
|---|
| 363 | } |
|---|
| 364 | } |
|---|
| 365 | |
|---|
| 366 | </script> |
|---|
| 367 | """ |
|---|
| 368 | |
|---|
| 369 | logHead = _head.replace("<!-- BODY -->", "<body style=\"cursor : default;\">") |
|---|
| 370 | testCaseShowRecordsHead = _head.replace("<!-- BODY -->", "<body style=\"cursor : default;\">") |
|---|
| 371 | testCaseShowGlyphsHead = _head.replace("<!-- BODY -->", "<body style=\"cursor : default;\" onload=\"renderAllCanvases();\" onresize=\"renderAllCanvases();\">") |
|---|
| 372 | testCaseShowGlyphsHead = testCaseShowGlyphsHead.replace("<!-- CANVAS SCRIPT -->", _testCaseCanvasJavaScript) |
|---|
| 373 | |
|---|
| 374 | tail = u"""</body> |
|---|
| 375 | </html> |
|---|
| 376 | """ |
|---|