| OLD | NEW |
| (Empty) |
| 1 // This is a node.js program to generate text-orientation-script test files. | |
| 2 var fs = require("fs"), | |
| 3 http = require("http"), | |
| 4 path = require("path"), | |
| 5 stream = require("stream"), | |
| 6 url = require("url"); | |
| 7 | |
| 8 var unicodeData = { | |
| 9 url: { | |
| 10 blocks: "http://www.unicode.org/Public/UCD/latest/ucd/Blocks.txt", | |
| 11 gc: "http://www.unicode.org/Public/UCD/latest/ucd/extracted/DerivedGener
alCategory.txt", | |
| 12 vo: "http://www.unicode.org/Public/vertical/revision-13/VerticalOrientat
ion-13.txt", | |
| 13 }, | |
| 14 get: function (source, formatter) { | |
| 15 formatter = formatter || this.formatAsArray; | |
| 16 var defer = Promise.defer(); | |
| 17 var buffer = ""; | |
| 18 var parser = new stream.Writable(); | |
| 19 parser._write = function (chunk, encoding, next) { | |
| 20 buffer += chunk; | |
| 21 next(); | |
| 22 }; | |
| 23 parser.on("finish", function () { | |
| 24 var results = null; | |
| 25 for (var line of buffer.split("\n")) | |
| 26 results = unicodeData.parseLine(line, formatter, results); | |
| 27 defer.resolve(results); | |
| 28 }); | |
| 29 var basename = path.basename(url.parse(source).path); | |
| 30 if (fs.existsSync(basename)) { | |
| 31 fs.createReadStream(basename) | |
| 32 .pipe(parser); | |
| 33 } else { | |
| 34 http.get(source, function (res) { | |
| 35 res.pipe(parser); | |
| 36 }); | |
| 37 } | |
| 38 return defer.promise; | |
| 39 }, | |
| 40 parseLine: function (line, formatter, results) { | |
| 41 if (!line.length || line[0] == "#") | |
| 42 return results; | |
| 43 var match = /([0-9A-F]+)(\.\.([0-9A-F]+))?\s*;\s*(\w+)/.exec(line); | |
| 44 if (!match) | |
| 45 throw new Error("Inavlid format: " + line); | |
| 46 var from = parseInt(match[1], 16); | |
| 47 var to = match[3] ? parseInt(match[3], 16) : from; | |
| 48 var value = match[4]; | |
| 49 return formatter(results, from, to, value); | |
| 50 }, | |
| 51 formatAsArray: function (results, from, to, value) { | |
| 52 results = results || []; | |
| 53 for (var code = from; code <= to; code++) | |
| 54 results[code] = value; | |
| 55 return results; | |
| 56 }, | |
| 57 formatAsRangesByValue: function (results, from, to, value) { | |
| 58 results = results || {}; | |
| 59 var list = results[value]; | |
| 60 if (!list) { | |
| 61 list = []; | |
| 62 results[value] = list; | |
| 63 } else { | |
| 64 var last = list[list.length - 1]; | |
| 65 if (last == from - 1) { | |
| 66 list[list.length - 1] = to; | |
| 67 return results; | |
| 68 } | |
| 69 } | |
| 70 list.push(from); | |
| 71 list.push(to); | |
| 72 return results; | |
| 73 }, | |
| 74 arrayFromRangesByValue: function (dict) { | |
| 75 var array = []; | |
| 76 for (var value in dict) { | |
| 77 var ranges = dict[value]; | |
| 78 for (var i = 0; i < ranges.length; i += 2) { | |
| 79 var to = ranges[i+1]; | |
| 80 for (var code = ranges[i]; code <= to; code++) | |
| 81 array[code] = value; | |
| 82 } | |
| 83 } | |
| 84 return array; | |
| 85 }, | |
| 86 codePointsFromRanges: function (ranges, gc) { | |
| 87 var codePoints = []; | |
| 88 for (var i = 0; i < ranges.length; i += 2) { | |
| 89 var code = ranges[i]; | |
| 90 var to = ranges[i+1]; | |
| 91 for (; code <= to; code++) { | |
| 92 if (code >= 0xD800 && code <= 0xDFFF) { // Surrogate Pairs | |
| 93 continue; | |
| 94 } | |
| 95 // To make tests smaller, omit some obvious ranges except the fi
rst and the last | |
| 96 if (code > 0x3400 && code < 0x4DB5 || // CJK Unified Ideographs
Extension A | |
| 97 code > 0x4E00 && code < 0x9FCC || // CJK Unified Ideographs
(Han) | |
| 98 code > 0xAC00 && code < 0xD7A3 || // Hangul Syllables | |
| 99 code > 0x20000 && code < 0x2A6D6 || // CJK Unified Ideograph
s Extension B | |
| 100 code > 0x2A700 && code < 0x2B734 || // CJK Unified Ideograph
s Extension C | |
| 101 code > 0x2B740 && code < 0x2B81D) { // CJK Unified Ideograph
s Extension D | |
| 102 continue; | |
| 103 } | |
| 104 var gc0 = gc[code][0]; | |
| 105 // General Category M* and C* are omitted as they're likely to n
ot render well | |
| 106 if (gc0 == "M" || gc0 == "C") | |
| 107 continue; | |
| 108 codePoints.push(code); | |
| 109 } | |
| 110 } | |
| 111 return codePoints; | |
| 112 }, | |
| 113 }; | |
| 114 | |
| 115 Promise.all([ | |
| 116 unicodeData.get(unicodeData.url.vo, unicodeData.formatAsRangesByValue), | |
| 117 unicodeData.get(unicodeData.url.gc), | |
| 118 unicodeData.get(unicodeData.url.blocks, unicodeData.formatAsRangesByValue), | |
| 119 ]).then(function (results) { | |
| 120 generate(results[0], results[1]); | |
| 121 | |
| 122 console.log("Writing unicode-data.js"); | |
| 123 var output = fs.openSync("unicode-data.js", "w"); | |
| 124 fs.writeSync(output, "var rangesByBlock = "); | |
| 125 fs.writeSync(output, JSON.stringify(results[2], null, " ")); | |
| 126 fs.writeSync(output, ";\n"); | |
| 127 fs.closeSync(output); | |
| 128 }).catch(function (e) { | |
| 129 console.log(e); | |
| 130 }); | |
| 131 | |
| 132 function generate(rangesByVO, gc) { | |
| 133 var template = fs.readFileSync("text-orientation-template.html", {encoding:"
utf-8"}) | |
| 134 .split("INSERT-DATA-HERE"); | |
| 135 | |
| 136 var codePointsByVO = {}; | |
| 137 for (var value in rangesByVO) | |
| 138 codePointsByVO[value] = unicodeData.codePointsFromRanges(rangesByVO[valu
e], gc); | |
| 139 | |
| 140 // single version | |
| 141 writeHtmlPage(codePointsByVO, template); | |
| 142 | |
| 143 // by-vo versions | |
| 144 var pageSize = 64 * 64; | |
| 145 var fileIndex = 0; | |
| 146 for (value in codePointsByVO) { | |
| 147 var codePoints = codePointsByVO[value]; | |
| 148 var pages = Math.floor(codePoints.length / pageSize) + 1; | |
| 149 // if (pages > 1) // by-vo combo versions | |
| 150 // writeHtmlPage(codePoints, template, value, 0, codePoints.length); | |
| 151 // by-vo paged versions | |
| 152 var index = 0; | |
| 153 for (var page = 1; index < codePoints.length; page++) { | |
| 154 fileIndex++; | |
| 155 var lim = Math.min(index + pageSize, codePoints.length); | |
| 156 index = writeHtmlPage(codePoints, template, fileIndex, value, index,
lim, page, pages); | |
| 157 } | |
| 158 } | |
| 159 } | |
| 160 | |
| 161 function writeHtmlPage(codePoints, template, fileIndex, value, index, lim, page,
pages) { | |
| 162 var path = "../../text-orientation-script-001"; | |
| 163 var title = "Test orientation of characters"; | |
| 164 var flags = "dom font"; | |
| 165 // if (fileIndex) | |
| 166 // path += "-" + padZero(fileIndex, 3); | |
| 167 if (fileIndex) | |
| 168 path += String.fromCharCode('a'.charCodeAt(0) + fileIndex - 1); | |
| 169 else | |
| 170 flags += " combo"; | |
| 171 if (value) { | |
| 172 title += " where vo=" + value; | |
| 173 var rangeText = (lim - index) + " code points in U+" + toHex(codePoints[
index]) + "-" + toHex(codePoints[lim-1]); | |
| 174 if (page && pages > 1) | |
| 175 rangeText = "#" + page + "/" + pages + ", " + rangeText; | |
| 176 title += " (" + rangeText + ")"; | |
| 177 } | |
| 178 path += ".html"; | |
| 179 console.log("Writing " + path + ": " + title); | |
| 180 var output = fs.openSync(path, "w"); | |
| 181 fs.writeSync(output, template[0].replace("<!--META-->", | |
| 182 '<title>CSS Writing Modes Test: ' + title + '.</title>\n' + | |
| 183 '<link rel="help" href="http://www.w3.org/TR/css-writing-modes-3/#text-o
rientation">\n' + | |
| 184 '<meta name="assert" content="' + title + '">\n' + | |
| 185 '<meta name="flags" content="' + flags + '">')); | |
| 186 if (value) { | |
| 187 index = writeValueBlock(output, value, codePoints, index, lim); | |
| 188 } else { | |
| 189 for (value in codePoints) { | |
| 190 var codePointsOfValue = codePoints[value]; | |
| 191 writeValueBlock(output, value, codePointsOfValue, 0, codePointsOfVal
ue.length); | |
| 192 } | |
| 193 } | |
| 194 fs.writeSync(output, template[1]); | |
| 195 fs.closeSync(output); | |
| 196 return index; | |
| 197 } | |
| 198 | |
| 199 function writeValueBlock(output, value, codePoints, index, lim) { | |
| 200 fs.writeSync(output, '<div data-vo="' + value + '" class="test">\n'); | |
| 201 var line = []; | |
| 202 for (; index < lim; index++) { | |
| 203 var code = codePoints[index]; | |
| 204 if (code >= 0x10000) { | |
| 205 code -= 0x10000; | |
| 206 line.push(code >>> 10 & 0x3FF | 0xD800); | |
| 207 code = 0xDC00 | code & 0x3FF; | |
| 208 } | |
| 209 line.push(code); | |
| 210 if (line.length >= 64) { | |
| 211 writeLine(output, line); | |
| 212 line = []; | |
| 213 } | |
| 214 } | |
| 215 if (line.length) | |
| 216 writeLine(output, line); | |
| 217 fs.writeSync(output, "</div>\n"); | |
| 218 return index; | |
| 219 } | |
| 220 | |
| 221 function writeLine(output, line) { | |
| 222 line = String.fromCharCode.apply(String, line) | |
| 223 .replace(/&/, "&") | |
| 224 .replace(/</, "<"); | |
| 225 fs.writeSync(output, "<div>" + line + "</div>\n"); | |
| 226 } | |
| 227 | |
| 228 function toHex(value) { | |
| 229 return padZero(value.toString(16).toUpperCase(), 4); | |
| 230 } | |
| 231 | |
| 232 function padZero(value, digits) { | |
| 233 if (value.length >= digits) | |
| 234 return value; | |
| 235 value = "0000" + value; | |
| 236 return value.substr(value.length - digits); | |
| 237 } | |
| OLD | NEW |