[전산물리 웹교재] 웹사이트를 책으로 만들기: 페이지 자르기
지킬(Jekyl)로 만든 깃허브 페이지(Github Pages)를 종이에 출력 가능한 책의 형태로 만듭니다. 여기서는
- 출판을 위한 새로운 지킬 레이아웃을 생성하고
- 프린트 환경을 위한 스타일시트를 작성하고
- 타입스크립트(자바스크립트)를 사용해 문서 내용을 페이지에 맞게 자르는
방법을 소개합니다. 인쇄는 크롬의 인쇄 기능(Ctrl+P)으로 테스트 하였습니다.
레이아웃 생성
HTML5 템플릿 자동 생성 도구를 사용하거나 하면 이런 태그가 있는 경우가 많습니다.
<meta name="viewport" content="width=device-width, initial-scale=1.0">
이 태그를 제외해야 브라우저의 너비에 따라 자동으로 글자 크기 등을 조절하지 않습니다. 동일한 출력 결과를 원한다면 이 태그를 제거해야 합니다.
{% capture CONTENTS %}
{% for chapter in site.data.toc -%}
{% assign chapter_dir = "/" | append: site.docsurl | append: chapter[0] | append: "/" -%}
{% assign doc = site.html_pages | where: 'dir', chapter_dir | first -%}
<div class="warpper chapter"><div class="page chapter"><span class="page-number"></span><h1 id="{{ doc.title | url_encode}}">{{ doc.title }}</h1></div>
<div class="page clauses"><span class="page-number"></span>{{ doc.content }}</div>
{% for clauses in chapter[1] -%}
{% assign clauses_dir = chapter_dir | append: clauses | append: "/" -%}
{% assign doc = site.html_pages | where: 'dir', clauses_dir | first -%}
<div class="page clauses"><span class="page-number"></span>{{ doc.content }}</div>
{% endfor -%}
</div>
{% endfor -%}
{% endcapture -%}
레이아웃 파일의 리퀴드 부분입니다. CONTENTS
변수에 페이지를 내용물을 담습니다. 문서의 내부구조는 [전산물리 웹교재] 리퀴드로 문서 목차 구현에서 소개한 내용을 따릅니다. div.warpper.chapter
안에 각 장의 제목을 담는 div.page.chapter
와 본문의 내용을 담는div.page.clauses
이 들어갑니다. 이 구조는 스타일시트에서 counter()
를 사용해 장과 절 번호를 표시할 때 사용하기 위한 구조입니다.
<span class="page-number"></span>
는 나중에 페이지번호를 표시하고 목차를 만들 때 사용합니다.
_layouts/textbook.html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- <meta name="viewport" content="width=device-width, initial-scale=1.0"> -->
<title>{{ site.title }}</title>
<!-- 스타일과 스크립트 -->
<script defer src="{{ "js/onload.js" | absolute_url }}"></script>
<link rel="stylesheet" href="{{ "css/main.css" | absolute_url }}">
</head>
<body class="print">
<main>
<article>
<div class="page title">
<h1 class="title">{{ site.bookname }}</h1>
<p class="desc">{{ site.description }}</p>
{% for author in site.authors -%}
<div class="author">
<span class="name">{{ author.name }}</span>
</div>
{% endfor -%}
</div>
<div class="page home">
{% assign home = site.docsurl | relative_url -%}
{% assign doc = site.html_pages | where: 'dir', home | first -%}
{{ doc.content }}
</div>
<div class="page toc">
<h1>차례</h1>
<ul id="textbook-toc"></ul>
</div>
{{ CONTENTS }}
</article>
</main>
</body>
</html>
본문의 내용이 들어가기 전에 표지로 들어갈 div.page.title
과 인트로가 들어갈 div.page.home
, 차례가 들어갈 div.page.toc
를 만들어 둡니다. 이후 실제 출판에 도전할 때 더 적당한 양식으로 제작하겠습니다. 지금은 그냥 PDF로 읽기 괜찮은 수준으로 만들었습니다.
docs/textbook.md
---
layout: textbook
---
그리고 이 레이아웃을 사용하는 빈 문서 하나를 생성합니다.
_config.yml
bookname: 책 이름
authors:
-
name: "저자 A"
-
name: "저자 B"
새로운 레이아웃에서 사용하는 사이트 속성을 추가합니다.
스타일 시트
스타일시트는 SASS로 작성하였습니다.
@media print
html, body
width: 210mm
height: 297mm
body.print
width: 100%
height: 100%
margin: 0
padding: 0
background-color: #FAFAFA
box-sizing: border-box
body.print div.page
width: 210mm
min-height: 297mm
padding: 20mm
margin: 10mm auto
border: 1px #D3D3D3 solid
border-radius: 5px
background: white
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1)
@media print
margin: 0 0 0 0
border: initial
border-radius: initial
width: initial
min-height: initial
box-shadow: initial
background: initial
page-break-after: always
A4 크기로 페이지를 나누고 각 페이지가 끝나는 부분에는 page-break-after: always
속성으로 새 페이지에 인쇄하도록 합니다.
스크립트
앞선 레이아웃은 문서 하나에 한 페이지를 배정합니다. 내용이 들어있는 페이지가 유독 길어지고 인쇄할때는 긴 부분이 잘려나오게 됩니다. 스크립트를 이용해 모든 페이지가 기준높이를 맞출 수 있도록 합니다. 모든 페이지가 적절한 여백을 가진 상태로 출력될 수 있도록 조정하는 역할을 합니다.
타입스크립트로 작성한 페이지 자르기 함수입니다.
function page_slice() {
// 기준 높이를 잡을 페이지로부터 랜더링한 높이를 기준 높이로 출력
let refHeight = window.getComputedStyle(document.querySelector("div.page.title")!).height
document.querySelectorAll("div.page:not(.title)")!.forEach(elem => {
let sub = 1
let refPage = elem
let newPage = document.createElement("div")
let child
newPage.setAttribute("class", elem.className)
while (true) {
// 모든 페이지가 기준 크기보다 작아질 때까지 페이지 분할 반복
if (refPage.lastElementChild) {
child = refPage.lastElementChild!.cloneNode(true)
} else {
break
}
if (window.getComputedStyle(refPage).height > refHeight) {
// 기존 페이지의 높이가 기준높이보다 크면
// 기존 페이지의 마지막 자식 노드를 새 페이지로 이동
if (newPage.childNodes[0]) {
newPage.insertBefore(child, newPage.childNodes[0])
} else {
newPage.appendChild(child)
}
refPage.removeChild(refPage.lastElementChild!)
} else if (window.getComputedStyle(newPage).height > refHeight) {
// 새 페이지의 높이가 기준 높이보다 크면 새 페이지 생성
sub++
newPage.innerHTML += '<span class="page-number"></span>'
refPage.after(newPage)
refPage = newPage
newPage = document.createElement("div")
newPage.setAttribute("class", elem.className)
} else if (newPage.innerHTML != "") {
// 새 페이지를 기존 페이지 뒤에 추가
newPage.innerHTML += '<span class="page-number"></span>'
refPage.after(newPage)
break
} else {
break
}
}
})
}
window.onload = () => {
if(document.querySelector("body.print")){
page_slice()
}
}
자바스크립트로는 이렇게 쓸 수 있습니다.
// 페이지 자르기 함수
function page_slice() {
// 기준 높이를 잡을 페이지로부터 랜더링한 높이를 기준 높이로 출력
var refHeight = window.getComputedStyle(document.querySelector("div.page.title")).height;
document.querySelectorAll("div.page:not(.title)").forEach(function (elem) {
var sub = 1;
var refPage = elem;
var newPage = document.createElement("div");
var child;
newPage.setAttribute("class", elem.className);
while (true) {
// 모든 페이지가 기준 크기보다 작아질 때까지 페이지 분할 반복
if (refPage.lastElementChild) {
child = refPage.lastElementChild.cloneNode(true);
}
else {
break;
}
if (window.getComputedStyle(refPage).height > refHeight) {
// 기존 페이지의 높이가 기준높이보다 크면
// 기존 페이지의 마지막 자식 노드를 새 페이지로 이동
if (newPage.childNodes[0]) {
newPage.insertBefore(child, newPage.childNodes[0]);
}
else {
newPage.appendChild(child);
}
refPage.removeChild(refPage.lastElementChild);
}
else if (window.getComputedStyle(newPage).height > refHeight) {
// 새 페이지의 높이가 기준 높이보다 크면 새 페이지 생성
sub++;
newPage.innerHTML += '<span class="page-number"></span>';
refPage.after(newPage);
refPage = newPage;
newPage = document.createElement("div");
newPage.setAttribute("class", elem.className);
}
else if (newPage.innerHTML != "") {
// 새 페이지를 기존 페이지 뒤에 추가
newPage.innerHTML += '<span class="page-number"></span>';
refPage.after(newPage);
break;
}
else {
break;
}
}
});
}
window.onload = function () {
if (document.querySelector("body.print")) {
page_slice();
}
};
문서의 길이에 따라 페이지 조정에는 상당한 시간이 걸릴 수 있습니다. 별도의 로딩 화면을 구성하는 것이 좋다고 생각합니다.
'프로젝트 > 전산물리학 웹교재' 카테고리의 다른 글
[전산물리 웹교재] 웹사이트를 책으로 만들기: 목차만들기 (0) | 2021.08.01 |
---|---|
[전산물리 웹교재] 리퀴드로 문서 목차 구현 (0) | 2021.07.26 |
[전산물리 웹교재] 지킬 개발환경 구성하기 (0) | 2021.07.26 |
[전산물리 웹교재] 기획 (0) | 2021.07.25 |