diff --git a/script.js b/script.js new file mode 100644 index 0000000..187e32d --- /dev/null +++ b/script.js @@ -0,0 +1,265 @@ +document.getElementById("info").innerHTML += "paper width (px)" + document.getElementById("paper").offsetWidth + "
"; +function css(element, property) { + return window.getComputedStyle(element, null).getPropertyValue(property); +} +var paper = document.getElementById('paper'); +var paperPage1 = document.getElementById('paper-page1'); +var font_family = css(paper, 'font-family'); +var font_size = css(paper, 'font-size'); +paper.addEventListener('input', getWordsWidth); +paper.addEventListener('keydown', function (e) { + if (e.key == " ") { //enter + e.preventDefault(); //Prevent default browser behavior + var sel; + var range; + sel = window.getSelection(); + range = sel.getRangeAt(0); + range.deleteContents(); + var textNode = document.createTextNode('\u00A0'); // no linebreaking-space + range.insertNode(textNode); + range.setStartAfter(textNode); + sel.addRange(range); + getWordsWidth(); + // paper.innerHTML+= paper.innerHTML; + //var currentPage = document.getElementById(getCaretCurrentPageId()); + //var currentRange = getCursorLengthRange(currentPage); + //var HTMLBeforeCaret = rangeToHTMLFragement(currentRange); + } +}); +// 如果游標在 contentdiveditable id "paper-page1" 裡面, +// 該函數返回 "paper-page1" +function getCaretCurrentPageId() { + var anchorNode = window.getSelection().anchorNode; + var currentNode = anchorNode; + while (currentNode.id === undefined || !(currentNode.id.match(/^paper-page\d+/))) { + currentNode = currentNode.parentNode; + } + return currentNode.id; +} +// 得到子節點(文字物件) +// ["dolore", [ "m ips", ["um si"], "t am"], "et"] +function getChildNodes(ele) { + var res_array; + res_array = []; + if (ele.nodeType == Node.TEXT_NODE) { + return ele; + } + else { + for (var i = 0; i < ele.childNodes.length; i++) { + res_array.push(getChildNodes((ele.childNodes[i]))); + } + return res_array; + } +} +// 平坦化陣列 +// [a, [b, c, d], [[e]]] => [a, b, c, d, e] +function flatten(arr) { + if (!Array.isArray(arr)) { + return arr; + } + else { + var result = []; + for (var i = 0; i < arr.length; i++) { + if (Array.isArray(arr[i])) { + result = result.concat(flatten(arr[i])); + } + else { + result.push(arr[i]); + } + } + return result; + } +} +/* 從第幾個字到第幾個字選 range. */ +function selecRengeFromCharToChar(i, j, ele) { + var range = document.createRange(); + var sel = document.getSelection(); + var flattened_ar = flatten(getChildNodes(ele)); + range.selectNodeContents(ele); + var ran_start = i; + var ran_end = j; + var ran_start_is_set = false; + for (var i = 0; i < flattened_ar.length; i++) { + var fragement_text_length = flattened_ar[i].textContent.length; + // 如果 ran_start > 碎片長度 + if (ran_start > fragement_text_length) { + ran_start -= fragement_text_length; + ran_end -= fragement_text_length; + // 如果 ran_start <= 碎片長度 + } + else if (ran_start <= fragement_text_length && ran_start_is_set == false) { + range.setStart(flattened_ar[i], ran_start); + ran_start_is_set = true; + // 如果 ran_end > 碎片長度 + if (ran_end > fragement_text_length) { + ran_end -= fragement_text_length; + // 如果 ran_end <= 碎片長度,setEnd 中止迴圈 + } + else { + range.setEnd(flattened_ar[i], ran_end); + break; + } + // 如果 ran_end > 碎片長度 + } + else if (ran_end > fragement_text_length && ran_start_is_set == true) { + ran_end -= fragement_text_length; + } + // 如果 ran_end <= 碎片長度,setEnd 中止迴圈 + else { + range.setEnd(flattened_ar[i], ran_end); + break; + } + } + var sel = window.getSelection(); + sel.removeAllRanges(); + sel.addRange(range); + return range; +} +/* +設置游標位置於 ele 要素的第 col 個字(不含標籤),例子: + +HTML
dolorem ipsum sit amet 1234
+setCaret(document.getElementById('paper-page1'),10) + V CARET +
dolorem ip|sum sit amet 1234
+*/ +function setCaret(ele, col) { + var range = document.createRange(); + var sel = document.getSelection(); + var flattened_ar = flatten(getChildNodes(ele)); // flattened array + range.selectNodeContents(ele); + var ran_start = col; + for (var i = 0; i < flattened_ar.length; i++) { + var fragement_text_length = flattened_ar[i].textContent.length; + if (ran_start > fragement_text_length) { + ran_start -= fragement_text_length; + } + else { + range.setEnd(flattened_ar[i], ran_start); + range.collapse(); + break; + } + } + var sel = window.getSelection(); + sel.removeAllRanges(); + sel.addRange(range); +} +// 將座標轉成座標所在位置的 range +function getCursorLengthRange(ele) { + var win = ele.ownerDocument.defaultView; + var range = win.getSelection().getRangeAt(0); + var cloneRange = range.cloneRange(); + cloneRange.selectNodeContents(ele); + cloneRange.setEnd(range.endContainer, range.endOffset); + return cloneRange; +} +// 將座標 range 轉成字串的長度 +function rangeToStringLen(range) { + return range.toString().length; +} +// 將座標位置 range 轉成 html 斷片 +function rangeToHTMLFragement(range) { + var nodes = range.cloneContents().childNodes; + var result = ""; + for (var i = 0; i < nodes.length; i++) { + var parentNode = nodes[i].parentNode; + var div = document.createElement("div"); + div.appendChild(parentNode); + if (typeof (nodes[i].outerHTML) === "undefined") { + result += nodes[i].textContent; + } + else { + result += nodes[i].outerHTML; + } + console.log("Parent Node" + div.outerHTML); + } + return result; +} +// 得到一個 word 或是 character 的寬度 +function getTextWidth(text, fontfamily, fontsize) { + console.log(text); + var decodedText = decodeEntities(text); + var canvas = document.createElement("canvas"); + var context = canvas.getContext("2d"); + context.font = fontsize + " " + fontfamily; + var width = context.measureText(decodedText).width; + return width; +} +// 得到字寬 +function getWordsWidth() { + document.getElementById('line-info').innerHTML = ""; + var array = paper.childNodes; + var res = []; + for (var i = 0; i < array.length; i++) { + if (array[i].nodeType != Node.TEXT_NODE) { + res.push(array[i]); + } + } + var innerArray = res.map(function (x) { return x.innerHTML; }); + var innerHTMLTotal = innerArray.reduce(function (x, y) { return (x + y); }); + var innerHTMLRemoveTags = innerHTMLTotal.replace(/[<][\/]?[^<]+[\/]?[>]/gi, ""); // 移除標籤 + var innerHTMLSplitted = innerHTMLRemoveTags.split(/( |\s| |[⺀-\u2efe\u3000-〾㇀-\u31ee㌀-㏾㐀-\u4dbe一-\u9ffe豈-\ufafe︰-﹎]|[\ud840-\ud868\ud86a-\ud86c][\udc00-\udfff]|\ud869[\udc00-\udede\udf00-\udfff]|\ud86d[\udc00-\udf3e\udf40-\udfff]|\ud86e[\udc00-\udc1e]|\ud87e[\udc00-\ude1e])/).filter(function (item) { return item != ""; }); + console.log(innerHTMLSplitted); + //var innerHTMLSplittedLength = innerHTMLSplitted.map((x)=>x.length) + //var innerHTMLSelectedRange = []; + /*var sum = 0 + for (var i=0; i rangeToHTMLFragement(selecRengeFromCharToChar(x[0],x[1],paper))) + + console.log("Range 結果"+innerHTMLSplittedRange);*/ + var textWidthList = innerHTMLSplitted.map(function (x) { return getTextWidth(x, font_family, font_size); }); + var lineInfoLength = []; + for (var i = 0; i < innerHTMLSplitted.length; i++) { + var object = { "content": innerHTMLSplitted[i], + "length": textWidthList[i] }; + document.getElementById("line-info").innerHTML += "(" + innerHTMLSplitted[i] + ","; + document.getElementById("line-info").innerHTML += textWidthList[i] + ")"; + lineInfoLength.push(object); + } + // document.getElementById("info").innerHTML += lineInfoLength; +} +// 將要在地幾個字插入符號,改為修正為在有標籤的 HTML 的片段中,第幾個不算標籤的字插入符號 +/* +> getrealcol(5, "example") +> 5 +> getrealcol(5, "example") +> 14 +*/ +function getrealcol(n, str) { + var arr = str.split(RegExp("(<[^<>]+>)")).map(function (x) { return ({ cont: x, len: x.length }); }); + var j = 0; + var k = 0; + for (var i = 0; i < arr.length; i++) { + if (arr[i].cont.match(/^[<]/)) { + continue; + } + else { + if (n > arr[i].len) { + n = n - arr[i].len; + } + else { + j = i; + k = n; + break; + } + } + ; + } + var res_col = 0; + for (var i = 0; i < j; i++) { + res_col += arr[i].len; + } + res_col += k; + return res_col; +} +// 解碼 &1234; 這種東西用 +function decodeEntities(encodedString) { + var textArea = document.createElement('textarea'); + textArea.innerHTML = encodedString; + return textArea.value; +} diff --git a/script.ts b/script.ts new file mode 100644 index 0000000..f71597b --- /dev/null +++ b/script.ts @@ -0,0 +1,318 @@ +document.getElementById("info").innerHTML += "paper width (px)" + document.getElementById("paper").offsetWidth + "
"; + +function css( element : HTMLElement, property : string ) { + return window.getComputedStyle( element, null ).getPropertyValue( property ); +} + +let paper = document.getElementById('paper'); +let paperPage1 = document.getElementById('paper-page1'); + + +var font_family = css( paper, 'font-family') +var font_size = css(paper, 'font-size') + +paper.addEventListener('input', getWordsWidth); + +paper.addEventListener('keydown', (e)=>{ + if(e.key==" "){ //enter + e.preventDefault(); //Prevent default browser behavior + var sel : Selection; + var range : Range; + sel = window.getSelection(); + range = sel.getRangeAt(0); + range.deleteContents(); + + var textNode = document.createTextNode('\u00A0'); // no linebreaking-space + range.insertNode(textNode); + range.setStartAfter(textNode); + sel.addRange(range); + + getWordsWidth(); + + // paper.innerHTML+= paper.innerHTML; + //var currentPage = document.getElementById(getCaretCurrentPageId()); + //var currentRange = getCursorLengthRange(currentPage); + //var HTMLBeforeCaret = rangeToHTMLFragement(currentRange); + } +}); + + +// 如果游標在 contentdiveditable id "paper-page1" 裡面, +// 該函數返回 "paper-page1" +function getCaretCurrentPageId(){ + var anchorNode = window.getSelection().anchorNode; + + var currentNode = anchorNode; + + while((currentNode as Element).id === undefined || !((currentNode as Element).id.match(/^paper-page\d+/))){ + currentNode = currentNode.parentNode; + } + return (currentNode as Element).id; + } + +// 得到子節點(文字物件) +// ["dolore", [ "m ips", ["um si"], "t am"], "et"] +function getChildNodes(ele : HTMLElement){ + var res_array + res_array = []; + if (ele.nodeType == Node.TEXT_NODE){ + return ele; + + }else{ + for (var i =0; i (ele.childNodes[i]))) + } + + return res_array + } +} + + // 平坦化陣列 + // [a, [b, c, d], [[e]]] => [a, b, c, d, e] + function flatten(arr){ + if (!Array.isArray(arr)){ + return arr; + } + else{ + var result = []; + for (var i=0;iele)); + + range.selectNodeContents((ele)); + var ran_start = i + var ran_end = j + var ran_start_is_set = false + + for (var i=0; i< flattened_ar.length;i++){ + var fragement_text_length = flattened_ar[i].textContent.length; + // 如果 ran_start > 碎片長度 + if(ran_start > fragement_text_length){ + ran_start -= fragement_text_length; + ran_end -= fragement_text_length; + + // 如果 ran_start <= 碎片長度 + }else if(ran_start <= fragement_text_length && ran_start_is_set == false){ + range.setStart(flattened_ar[i], ran_start); + ran_start_is_set = true + // 如果 ran_end > 碎片長度 + if (ran_end > fragement_text_length){ + ran_end -= fragement_text_length; + // 如果 ran_end <= 碎片長度,setEnd 中止迴圈 + }else{ + range.setEnd(flattened_ar[i], ran_end); + break; + } + // 如果 ran_end > 碎片長度 + }else if (ran_end > fragement_text_length && ran_start_is_set == true){ + ran_end -= fragement_text_length; + } + // 如果 ran_end <= 碎片長度,setEnd 中止迴圈 + else{ + range.setEnd(flattened_ar[i], ran_end); + break; + } + } + + var sel = window.getSelection(); + sel.removeAllRanges(); + sel.addRange(range); + + return range + +} + + +/* +設置游標位置於 ele 要素的第 col 個字(不含標籤),例子: + +HTML
dolorem ipsum sit amet 1234
+setCaret(document.getElementById('paper-page1'),10) + V CARET +
dolorem ip|sum sit amet 1234
+*/ +function setCaret(ele: ElementContentEditable, col : number){ + var range = document.createRange(); + var sel = document.getSelection(); + + + + var flattened_ar = flatten(getChildNodes(ele)); // flattened array + + range.selectNodeContents((ele)); + var ran_start = col + + for (var i=0; i< flattened_ar.length;i++){ + var fragement_text_length = flattened_ar[i].textContent.length; + if(ran_start > fragement_text_length){ + ran_start -= fragement_text_length; + }else{ + range.setEnd(flattened_ar[i], ran_start); + range.collapse(); + break; + } + } + + var sel = window.getSelection(); + sel.removeAllRanges(); + sel.addRange(range); +} + +// 將座標轉成座標所在位置的 range +function getCursorLengthRange(ele: HTMLElement){ + let win = ele.ownerDocument.defaultView; + let range = win.getSelection().getRangeAt(0); + + let cloneRange = range.cloneRange(); + cloneRange.selectNodeContents(ele); + cloneRange.setEnd(range.endContainer, range.endOffset); + + return cloneRange; + +} + +// 將座標 range 轉成字串的長度 +function rangeToStringLen(range : Range){ + return range.toString().length; +} + +// 將座標位置 range 轉成 html 斷片 +function rangeToHTMLFragement(range : Range){ + var nodes =range.cloneContents().childNodes; + var result = "" + for(var i=0;i nodes[i].parentNode); + let div=document.createElement("div"); + div.appendChild(parentNode); + if (typeof (( nodes[i]).outerHTML) === "undefined"){ + result += nodes[i].textContent;} + else{ + result += ( nodes[i]).outerHTML;} + console.log("Parent Node"+div.outerHTML); + } + return result; + } + +// 得到一個 word 或是 character 的寬度 +function getTextWidth(text, fontfamily, fontsize) { + console.log(text) + var decodedText = decodeEntities(text) + let canvas = document.createElement("canvas"); + var context = canvas.getContext("2d"); + context.font = fontsize + " " + fontfamily; + let width = context.measureText(decodedText).width; + return width; + } + +// 得到字寬 +function getWordsWidth(){ + document.getElementById('line-info').innerHTML = ""; + + var array = paper.childNodes; + var res = []; + + for (var i=0;ix.innerHTML); + + var innerHTMLTotal = innerArray.reduce((x,y)=>(x+y)); + + var innerHTMLRemoveTags = innerHTMLTotal.replace(/[<][\/]?[^<]+[\/]?[>]/gi,"") // 移除標籤 + + var innerHTMLSplitted = innerHTMLRemoveTags.split(/( |\s| |[⺀-\u2efe\u3000-〾㇀-\u31ee㌀-㏾㐀-\u4dbe一-\u9ffe豈-\ufafe︰-﹎]|[\ud840-\ud868\ud86a-\ud86c][\udc00-\udfff]|\ud869[\udc00-\udede\udf00-\udfff]|\ud86d[\udc00-\udf3e\udf40-\udfff]|\ud86e[\udc00-\udc1e]|\ud87e[\udc00-\ude1e])/).filter(item => item != "") + + console.log(innerHTMLSplitted); + //var innerHTMLSplittedLength = innerHTMLSplitted.map((x)=>x.length) + + //var innerHTMLSelectedRange = []; + + /*var sum = 0 + for (var i=0; i rangeToHTMLFragement(selecRengeFromCharToChar(x[0],x[1],paper))) + + console.log("Range 結果"+innerHTMLSplittedRange);*/ + + + let textWidthList = innerHTMLSplitted.map(x => getTextWidth(x,font_family, font_size)); + + + var lineInfoLength = []; + + for (var i=0; i getrealcol(5, "example") +> 5 +> getrealcol(5, "example") +> 14 +*/ +function getrealcol(n, str) { + var arr = str.split(RegExp("(<[^<>]+>)")).map((x) => ({ cont: x, len: x.length })); + + var j = 0; + var k = 0; + for (var i = 0; i < arr.length; i++) { + if (arr[i].cont.match(/^[<]/)) { continue; } + else { + if (n > arr[i].len) { n = n - arr[i].len; } + else { j = i; k = n; break; } + }; + } + + var res_col = 0; + + for (var i = 0; i < j; i++) { + res_col += arr[i].len; + } + + res_col += k; + + return res_col; + } + +// 解碼 &1234; 這種東西用 +function decodeEntities(encodedString) { + var textArea = document.createElement('textarea'); + textArea.innerHTML = encodedString; + return textArea.value; +} \ No newline at end of file diff --git a/test.html b/test.html new file mode 100644 index 0000000..d8a990e --- /dev/null +++ b/test.html @@ -0,0 +1,19 @@ + + + + typesetting engine test + + + + +
dolorem ipsum sit amet Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque tristique rutrum enim, auctor pharetra ligula sagittis volutpat. Donec pulvinar velit varius neque mattis suscipit. Fusce blandit vitae dui in accumsan. Aenean aliquam nisi at aliquam egestas. Maecenas placerat sodales purus, vitae imperdiet tellus lobortis sed. Proin aliquet lacus ut augue ullamcorper, vel commodo sem posuere. Vestibulum viverra lacus pretium nisl gravida commodo. Suspendisse vitae mauris ut justo pharetra lacinia. Phasellus ex dui, pharetra sit amet elit non, finibus iaculis dolor. Donec at orci consequat, tristique nulla a, luctus risus. Suspendisse eget felis euismod, lacinia diam in, mollis metus. Nunc eu tristique eros, ac venenatis massa. In vel ligula sit amet lacus imperdiet volutpat. Phasellus tincidunt tortor nec nisi luctus, vulputate tincidunt massa rutrum. Curabitur leo purus, dictum ac felis eget, porttitor vulputate felis.
+
+
+ + + +