SVG はテキストのページ付けに適している可能性があります
テキストの画像だけを表示するキャンバスとは異なり、SVG テキストは実際にはテキストです。
SVG テキストは読み取り、選択、検索が可能です。
SVG テキストはネイティブでは自動ラップされませんが、これは JavaScript を使用して簡単に修正できます。
ページの書式設定は JavaScript で行われるため、柔軟なページ サイズが可能です。
ページネーションは、ブラウザ依存のフォーマットに依存しません。
テキストのダウンロードは小さくて効率的です。現在のページのテキストのみをダウンロードする必要があります。
SVG ページネーションの詳細とデモは次のとおりです。
http://jsfiddle.net/m1erickson/Lf4Vt/
パート 1: サーバー上のデータベースから約 1 ページ分の単語を効率的にフェッチする
テキスト全体をデータベースに 1 行あたり 1 語ずつ保存します。
各行 (単語) は、単語の順序で順次インデックス付けされます (単語 #1 はインデックス ==1、単語 #2 はインデックス ==2 など)。
たとえば、これはテキスト全体を適切な語順にフェッチします。
// select the entire text of Romeo and Juliet
// “order by wordIndex” causes the words to be in proper order
Select word from RomeoAndJuliet order by wordIndex
フォーマットされたページに約 250 語が含まれていると仮定すると、このデータベース クエリはページ #1 のテキストの最初の 250 語を取得します。
// select the first 250 words for page#1
Select top 250 word from RomeoAndJuliet order by wordIndex
今良い部分です!
フォーマット後にページ#1が212語を使用したとしましょう。次に、ページ #2 を処理する準備ができたら、単語 #213 から始まるさらに 250 単語を取得できます。これにより、迅速かつ効率的なデータ取得が可能になります。
// select 250 more words for page#2
// “where wordIndex>212” causes the fetched words
// to begin with the 213th word in the text
Select top 250 word from RomeoAndJuliet order by wordIndex where wordIndex>212
パート 2: フェッチされた単語を、指定されたページ幅に収まるテキスト行にフォーマットする
テキストの各行には、指定されたページを埋めるのに十分な単語が含まれている必要がありますが、それ以上は含まれていません。
行番号 1 を 1 つの単語で開始し、テキストが指定されたページ幅に収まるまで単語を 1 つずつ追加します。
最初の行が適合された後、行の高さだけ下に移動し、行 #2 を開始します。
行に単語を合わせるには、行に追加された各単語を測定する必要があります。次の単語が行幅を超える場合、余分な単語は次の行に移動されます。
context.measureText
単語は、Html Canvasesメソッド を使用して測定できます。
このコードは一連の単語 (データベースからフェッチされた 250 単語など) を受け取り、ページ サイズを満たすためにできるだけ多くの単語をフォーマットします。
maxWidth
テキスト行の最大ピクセル幅です。
maxLines
ページに収まる最大行数です。
function textToLines(words,maxWidth,maxLines,x,y){
var lines=[];
while(words.length>0 && lines.length<=maxLines){
var line=getOneLineOfText(words,maxWidth);
words=words.splice(line.index+1);
lines.push(line);
wordCount+=line.index+1;
}
return(lines);
}
function getOneLineOfText(words,maxWidth){
var line="";
var space="";
for(var i=0;i<words.length;i++){
var testWidth=ctx.measureText(line+" "+words[i]).width;
if(testWidth>maxWidth){return({index:i-1,text:line});}
line+=space+words[i];
space=" ";
}
return({index:words.length-1,text:line});
}
パート 3: SVG を使用してテキスト行を表示する
SVG Text 要素は、読み取り、選択、検索が可能な真の html 要素です。
SVG Text 要素内の個々のテキスト行は、SVG Tspan 要素を使用して表示されます。
このコードは、パート 2 でフォーマットされたテキスト行を取得し、SVG を使用してテキストのページとして行を表示します。
function drawSvg(lines,x){
var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
var sText = document.createElementNS('http://www.w3.org/2000/svg', 'text');
sText.setAttributeNS(null, 'font-family', 'verdana');
sText.setAttributeNS(null, 'font-size', "14px");
sText.setAttributeNS(null, 'fill', '#000000');
for(var i=0;i<lines.length;i++){
var sTSpan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan');
sTSpan.setAttributeNS(null, 'x', x);
sTSpan.setAttributeNS(null, 'dy', lineHeight+"px");
sTSpan.appendChild(document.createTextNode(lines[i].text));
sText.appendChild(sTSpan);
}
svg.appendChild(sText);
$page.append(svg);
}
デモ リンクが壊れた場合に備えて、完全なコードを次に示します。
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
.page{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.createElement("canvas");
var ctx=canvas.getContext("2d");
ctx.font="14px verdana";
var pageWidth=250;
var pageHeight=150;
var pagePaddingLeft=10;
var pagePaddingRight=10;
var approxWordsPerPage=500;
var lineHeight=18;
var maxLinesPerPage=parseInt(pageHeight/lineHeight)-1;
var x=pagePaddingLeft;
var y=lineHeight;
var maxWidth=pageWidth-pagePaddingLeft-pagePaddingRight;
var text="Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.";
// # words that have been displayed
//(used when ordering a new page of words)
var wordCount=0;
// size the div to the desired page size
$pages=$(".page");
$pages.width(pageWidth)
$pages.height(pageHeight);
// Test: Page#1
// get a reference to the page div
var $page=$("#page");
// use html canvas to word-wrap this page
var lines=textToLines(getNextWords(wordCount),maxWidth,maxLinesPerPage,x,y);
// create svg elements for each line of text on the page
drawSvg(lines,x);
// Test: Page#2 (just testing...normally there's only 1 full-screen page)
var $page=$("#page2");
var lines=textToLines(getNextWords(wordCount),maxWidth,maxLinesPerPage,x,y);
drawSvg(lines,x);
// Test: Page#3 (just testing...normally there's only 1 full-screen page)
var $page=$("#page3");
var lines=textToLines(getNextWords(wordCount),maxWidth,maxLinesPerPage,x,y);
drawSvg(lines,x);
// fetch the next page of words from the server database
// (since we've specified the starting point in the entire text
// we only have to download 1 page of text as needed
function getNextWords(nextWordIndex){
// Eg: select top 500 word from romeoAndJuliet
// where wordIndex>=nextwordIndex
// order by wordIndex
//
// But here for testing, we just hardcode the entire text
var testingText="Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.";
var testingWords=testingText.split(" ");
var words=testingWords.splice(nextWordIndex,approxWordsPerPage);
//
return(words);
}
function textToLines(words,maxWidth,maxLines,x,y){
var lines=[];
while(words.length>0 && lines.length<=maxLines){
var line=getLineOfText(words,maxWidth);
words=words.splice(line.index+1);
lines.push(line);
wordCount+=line.index+1;
}
return(lines);
}
function getLineOfText(words,maxWidth){
var line="";
var space="";
for(var i=0;i<words.length;i++){
var testWidth=ctx.measureText(line+" "+words[i]).width;
if(testWidth>maxWidth){return({index:i-1,text:line});}
line+=space+words[i];
space=" ";
}
return({index:words.length-1,text:line});
}
function drawSvg(lines,x){
var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
var sText = document.createElementNS('http://www.w3.org/2000/svg', 'text');
sText.setAttributeNS(null, 'font-family', 'verdana');
sText.setAttributeNS(null, 'font-size', "14px");
sText.setAttributeNS(null, 'fill', '#000000');
for(var i=0;i<lines.length;i++){
var sTSpan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan');
sTSpan.setAttributeNS(null, 'x', x);
sTSpan.setAttributeNS(null, 'dy', lineHeight+"px");
sTSpan.appendChild(document.createTextNode(lines[i].text));
sText.appendChild(sTSpan);
}
svg.appendChild(sText);
$page.append(svg);
}
}); // end $(function(){});
</script>
</head>
<body>
<h4>Text split into "pages"<br>(Selectable & Searchable)</h4>
<div id="page" class="page"></div>
<h4>Page 2</h4>
<div id="page2" class="page"></div>
<h4>Page 3</h4>
<div id="page3" class="page"></div>
</body>
</html>