Skip to content

Instantly share code, notes, and snippets.

@nanto
Created February 19, 2010 04:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nanto/308409 to your computer and use it in GitHub Desktop.
Save nanto/308409 to your computer and use it in GitHub Desktop.
A userChrome.js script to read selected text with js-ctypes.
// ==UserScript==
// @name Read Selection
// @namespace http://nanto.asablo.jp/blog/
// @description Read selection text.
// @include chrome://browser/content/browser.xul
// @author nanto_vi
// ==/UserScript==
({
// http://www.a-quest.com/products/aquestalk2.html
AQUES_TALK_2_DA_PATH: 'C:\\AquesTalk2\\bin\\AquesTalk2Da.dll',
// http://kakasi.namazu.org/
KAKASI_PATH: 'C:\\kakasi\\lib\\kakasi.dll',
CRT_PATH: 'msvcr71',
READ_SELECTION_LABEL: '選択範囲のテキストを読み上げ',
READ_SELECTION_KEY: 'R',
NATIVE_ENCODING: 'Shift_JIS',
menuitem: null,
init: function RS_init() {
default xml namespace =
"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
let range = document.createRange();
range.setStartBefore(document.getElementById('context-sep-viewsource'));
range.collapse(true);
let label = this.READ_SELECTION_LABEL;
let key = this.READ_SELECTION_KEY;
if ('あ'.length !== 1) {
label = this.decode(label, 'UTF-8');
key = this.decode(key, 'UTF-8');
}
let menuitem = range.createContextualFragment(
<menuitem label={ label } accesskey={ key }/>.toXMLString()
).firstChild;
range.insertNode(menuitem);
menuitem.addEventListener('command', this, false);
menuitem.parentNode.addEventListener('popupshowing', this, false);
this.menuitem = menuitem;
},
handleEvent: function RS_handleEvent(event) {
switch (event.type) {
case 'command':
this.read(getBrowserSelection());
break;
case 'popupshowing':
this.menuitem.collapsed = !gContextMenu.isTextSelected;
break;
}
},
ctypes: Cu.import('resource://gre/modules/ctypes.jsm', {}).ctypes,
read: function RS_read(text) {
text = this.toPhonetic(this.toKana(text));
let ctypes = this.ctypes;
let aquesTalk2Da = ctypes.open(this.AQUES_TALK_2_DA_PATH);
try {
let playSync = aquesTalk2Da.declare('AquesTalk2Da_PlaySync',
ctypes.stdcall_abi,
ctypes.int32_t,
ctypes.string,
ctypes.int32_t,
ctypes.int32_t);
let r = playSync(this.encode(text, this.NATIVE_ENCODING), 100, 0);
if (r)
throw new Error('AquesTalk2Da_PlaySync failed: ' + r);
} finally {
aquesTalk2Da.close();
}
},
// http://www.a-quest.com/download/manual/siyo_onseikigou.pdf
toPhonetic: function RS_toPhonetic(text) {
return text.replace(/[^0-9\-,;\u3001\u3002\u3041-\u30fa\u30fc\uff1f]+/g, '/')
.replace(/[0-9-]+/g, '<NUM VAL=$&>');
},
toKana: function RS_toKana(text) {
let ctypes = this.ctypes;
let kakasi = ctypes.open(this.KAKASI_PATH);
try {
let kakasi_getopt_argv = kakasi.declare('kakasi_getopt_argv',
ctypes.default_abi,
ctypes.int32_t,
ctypes.int32_t,
ctypes.ustring);
let args = this.createArgs('kakasi', '-JH');
let r = kakasi_getopt_argv(args.count, args.value);
if (r)
throw new Error('kakasi_getopt_argv failed: ' + r);
let kakasi_do = kakasi.declare('kakasi_do',
ctypes.default_abi,
ctypes.uint32_t,
ctypes.string);
let kanaPointer = kakasi_do(this.encode(text, this.NATIVE_ENCODING));
let kana = this.decode(this.getCString(kanaPointer), this.NATIVE_ENCODING);
let kakasi_free = kakasi.declare('kakasi_free',
ctypes.default_abi,
ctypes.int32_t,
ctypes.uint32_t);
kakasi_free(kanaPointer);
return kana;
} finally {
kakasi.close();
}
},
createArgs: function RS_createArgs() {
let args = Array.map(arguments, function (argString) {
let ansiArg = this.encode(argString, this.NATIVE_ENCODING) + '\0';
return ansiArg.replace(/[\s\S]{1,2}/g, function (s) {
return String.fromCharCode(s.charCodeAt(0) |
(s.charCodeAt(1) << 8));
});
}, this);
args.value = args.map(function (arg) {
let pointer = this.getPointer(arg);
return String.fromCharCode(pointer & 0xffff, pointer >>> 16);
}, this).join('');
args.count = args.length;
return args;
},
getPointer: function RS_getPointer(blobString) {
if (!blobString) return 0;
let ctypes = this.ctypes;
let crt = ctypes.open(this.CRT_PATH);
try {
let memchr = crt.declare('memchr',
ctypes.stdcall_abi,
ctypes.int32_t,
ctypes.ustring,
ctypes.int32_t,
ctypes.uint32_t);
let firstByte = blobString.charCodeAt(0) & 0xff;
return memchr(blobString, firstByte, 1);
} finally {
crt.close();
}
},
getCString: function RS_getCString(pointer) {
let ctypes = this.ctypes;
let crt = ctypes.open(this.CRT_PATH);
try {
let strlen = crt.declare('strlen',
ctypes.stdcall_abi,
ctypes.uint32_t,
ctypes.uint32_t);
let length = strlen(pointer);
let memcpy = crt.declare('memcpy',
ctypes.stdcall_abi,
ctypes.uint32_t,
ctypes.ustring,
ctypes.uint32_t,
ctypes.uint32_t);
let buffer = new Array(((length + 1) >> 1) + 1).join('\0');
memcpy(buffer, pointer, length);
return buffer.replace(/[\s\S]/g, function (c) {
let n = c.charCodeAt(0);
return String.fromCharCode(n & 0xff, n >>> 8);
}).substring(0, length);
} finally {
crt.close();
}
},
encode: function RS_encode(string, encoding) {
let converter = Cc['@mozilla.org/intl/scriptableunicodeconverter'].
createInstance(Ci.nsIScriptableUnicodeConverter);
converter.charset = encoding;
return converter.ConvertFromUnicode(string);
},
decode: function RS_decode(cString, encoding) {
let converter = Cc['@mozilla.org/intl/scriptableunicodeconverter'].
createInstance(Ci.nsIScriptableUnicodeConverter);
converter.charset = encoding;
return converter.ConvertToUnicode(cString);
},
}).init();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment