add the files

This commit is contained in:
Tan, Kian-ting 2021-05-25 20:30:40 +08:00
parent 151acc05d2
commit 37cf8c8b82
3 changed files with 602 additions and 0 deletions

265
script.js Normal file
View file

@ -0,0 +1,265 @@
document.getElementById("info").innerHTML += "paper width (px)" + document.getElementById("paper").offsetWidth + "<br/>";
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 <div>dolore<i>m ips<b>um si</b>t am</i>et&nbsp;1234<div>
setCaret(document.getElementById('paper-page1'),10)
V CARET
<div>dolore<i>m ip|s<b>um si</b>t am</i>et&nbsp;1234<div>
*/
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|&nbsp;|[⺀-\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<innerHTMLSplittedLength.length;i++){
var head = sum;
var tail = sum + innerHTMLSplittedLength[i];
innerHTMLSelectedRange.push([head, tail]);
sum+= innerHTMLSplittedLength[i];
}*/
/*var innerHTMLSplittedRange = innerHTMLSelectedRange.map((x) => 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, "e<em>xam</em>ple")
> 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;
}

318
script.ts Normal file
View file

@ -0,0 +1,318 @@
document.getElementById("info").innerHTML += "paper width (px)" + document.getElementById("paper").offsetWidth + "<br/>";
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.length;i++){
res_array.push(getChildNodes(<HTMLElement>(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 : number, j : number, ele: ElementContentEditable){
var range = document.createRange();
var sel = document.getSelection();
var flattened_ar = flatten(getChildNodes(<HTMLElement>ele));
range.selectNodeContents((<Node><unknown>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 <div>dolore<i>m ips<b>um si</b>t am</i>et&nbsp;1234<div>
setCaret(document.getElementById('paper-page1'),10)
V CARET
<div>dolore<i>m ip|s<b>um si</b>t am</i>et&nbsp;1234<div>
*/
function setCaret(ele: ElementContentEditable, col : number){
var range = document.createRange();
var sel = document.getSelection();
var flattened_ar = flatten(getChildNodes(<HTMLElement>ele)); // flattened array
range.selectNodeContents((<Node><unknown>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.length;i++){
let parentNode = (<DocumentFragment> nodes[i].parentNode);
let div=document.createElement("div");
div.appendChild(parentNode);
if (typeof ((<HTMLElement> nodes[i]).outerHTML) === "undefined"){
result += nodes[i].textContent;}
else{
result += (<HTMLElement> 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;i<array.length;i++){
if( array[i].nodeType != Node.TEXT_NODE){
res.push(array[i]);
}
}
var innerArray = res.map((x)=>x.innerHTML);
var innerHTMLTotal = innerArray.reduce((x,y)=>(x+y));
var innerHTMLRemoveTags = innerHTMLTotal.replace(/[<][\/]?[^<]+[\/]?[>]/gi,"") // 移除標籤
var innerHTMLSplitted = innerHTMLRemoveTags.split(/( |\s|&nbsp;|[⺀-\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<innerHTMLSplittedLength.length;i++){
var head = sum;
var tail = sum + innerHTMLSplittedLength[i];
innerHTMLSelectedRange.push([head, tail]);
sum+= innerHTMLSplittedLength[i];
}*/
/*var innerHTMLSplittedRange = innerHTMLSelectedRange.map((x) => 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<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, "e<em>xam</em>ple")
> 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;
}

19
test.html Normal file
View file

@ -0,0 +1,19 @@
<html>
<head>
<meta charset="UTF-8">
<title>typesetting engine test</title>
</head>
<body>
<span id="info"></span>
<span id="line-info"></span>
<div id="paper" style="background:#e0e0e0; font-family:Latin Modern Roman; font-size: 14pt;" width="100%" height="100%" contenteditable="true"><div id="paper-page1"
style="background:white;width:210mm;height:297mm;top:15%;left:50%; margin-left:105mm; margin-top:10px; margin-bottom:10px; border:1px solid #000000;"
contenteditable="true">dolore<i>m</i> <i>ips<b>um</b></i> <i><b>si</b>t</i> <i>am</i>et 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. </div>
<div id="paper-page2"
style="background:white;width:210mm;height:297mm;margin-left:105mm;margin-top:10px; margin-bottom:10px; border:1px solid #000000;"
contenteditable="true"></div>
</div>
</body>
<script type="text/javascript" src="script.js"></script>
</html>