Cloud Typesetting Engine

The typesetting engine
made for serious books.

Built by a team that wrote its own programming language for publishing layout.

Cloud-native, engineered for print-grade output.
Free to start.

To move a single word to the next line,
we recompute the entire paragraph.

Why ATLAS

The depth a serious book deserves.

Built by TAFO

We published a book ourselves. Then we built the tool we needed.

ATLAS began after TAFO published an essay as both an ebook and a print book. Finishing the manuscript turned out to be only the start. The ebook required Sigil, and the print interior required InDesign.

Outsourcing was expensive. Doing it ourselves meant learning a production process far removed from writing. At first, we thought publishing our finished source files might help other writers. But even with the files in hand, every writer would still have to learn the same software and process from scratch.

So we decided to build a tool instead of handing over files. It had to let a writer add a manuscript, review a properly typeset interior, and export a print-ready PDF. ATLAS is the tool we needed and could not find.

Written in Topaz

We built a language, then wrote ATLAS's line-breaking rules in it.

Topaz is our general-purpose programming language. In ATLAS, it spells out the decisions behind line breaking, from kinsoku classification and line badness to spacing penalties and whole-paragraph optimization. The complete source below is 나쁨도.tpz, which turns the balance of a line into a score. Names such as 여유, 목표너비, and 글자수 are used directly in the calculation.

The grammar, playground, and compiler binaries are available at topaz.ooo.

나쁨도.tpz
// ATLAS 나쁨도. 한 줄의 나쁨도를 고정소수로 매기는 명세다(fixed-point line badness).
// 통합 줄바꿈기 `줄바꿈.tpz`의 2단계(한 줄이 목표너비에 견줘 얼마나 헐겁거나 빡빡한지를 점수로
// 매기는 일)를 따로 떼어 적었으며, 영어 PoC badness.tpz의 한국어 식별자 번역이다.
// 동작은 바이트 단위로 같다(badness_fp.rs ≡ ATLAS Rust).
// 입출력. 표준입력으로 "<너비>|<목표너비>|<글자수>|<공백수>|<분할종류>|<마지막인가(0/1)>" 꼴의 줄을 받아 줄마다 나쁨도(B 단위)를 낸다.

let R = 100000000   // 1e8 비율 스케일 (ratio scale)
let B = 1000000     // 1e6 나쁨도 스케일 (badness scale)

function 반올림나눗셈(분자: int, 분모: int) -> int {   // round-half-up, 음이 아닌 값만
  let= 분자 / 분모
  let 나머지 = 분자 % 분모
  if 나머지 >= (분모 + 1) / 2 { 몫 + 1 } else { 몫 }
}
function 절댓값(값: int) -> int { if< 0 { -값 } else { 값 } }

function 나쁨도(너비: int, 목표너비: int, 글자수: int, 공백수: int, 분할종류: string, 마지막인가: bool) -> int {
  let 여유 = 목표너비 - 너비
  let 절대여유 = 절댓값(여유)
  let 원시R = if 절대여유 >= 4 * 목표너비 { 4 * R } else { 절대여유 * R / 목표너비 }
  let 조정상한센티 = if 여유 >= 0 { 100 + 공백수 * 45 } else { 100 + 공백수 * 25 }
  let 비례R = 반올림나눗셈(원시R * 100, 조정상한센티)
  let 제곱 = 비례R * 비례R
  let= 반올림나눗셈(제곱, R)
  let mut 기본B = 반올림나눗셈(몫 * (1000 * B), R)
  if 마지막인가 {
    기본B = 반올림나눗셈(기본B * 2, 5)
    if 너비 * 100 < 목표너비 * 52 && 글자수 > 0 { 기본B += 500 * B }
  }
  if 여유 < 0 {
    기본B += 20000 * B + 반올림나눗셈(20000 * B * 비례R, R)
  }
  if 분할종류 == "latin_hyphen" { 기본B += 65 * B }
  if 분할종류 == "hard_fallback" { 기본B += 50000 * B }
  if 글자수 <= 2 { 기본B += 275 * B }
  기본B += 10 * B
  기본B
}

function 처리(입력: string) -> string {
  let mut 출력 = ""
  forin 입력.split("\n") {
    let= 줄.split("|")
    let 너비 = toInt(칸[0]) ?? 0
    let 목표 = toInt(칸[1]) ?? 0
    let 글자수 = toInt(칸[2]) ?? 0
    let 공백수 = toInt(칸[3]) ?? 0
    let 분할종류 = 칸[4]
    let 마지막인가 = 칸[5] == "1"
    let= 나쁨도(너비, 목표, 글자수, 공백수, 분할종류, 마지막인가)
    출력 = if 출력 == "" { "{}" } else { "{출력}\n{}" }
  }
  출력
}

print(처리(input()))

Complete source of 나쁨도.tpz

How it works

From manuscript to interior PDF
in three steps.

  1. 01

    Paste your manuscript

    Plain text, Markdown, or direct paste — whichever way you write.

  2. 02

    Automatic typesetting

    ATLAS solves each paragraph with publication-grade typography rules to find the optimal line breaks and pagination.

  3. 03

    Export the interior PDF

    An interior PDF, typeset to the trim you chose.

Output Specimen

The fastest way to show what we mean.

A book spread typeset with ATLAS

An interior spread typeset entirely with ATLAS

Trim presets

Ships with Korean trim presets, including Bookk 46.