~ehmry/zua

c9511f99db4bfd58ea361ae8422e2c3a335653ab — Emery Hemingway 2 years ago 6ec86cf
Move xhtml rendering here
2 files changed, 103 insertions(+), 2 deletions(-)

A src/private/render/xhtml.nim
M src/zua.nim
A src/private/render/xhtml.nim => src/private/render/xhtml.nim +101 -0
@@ 0,0 1,101 @@
# SPDX-FileCopyrightText: ☭ 2021 Emery Hemingway
# SPDX-License-Identifier: Unlicense

import std/sequtils
import pixie, pixie/systemtypefaces
import svui

type Typesetting* = object
  h1, h2, h3, p: Font

proc newFont(typeface: Typeface, size: float32): Font =
  result = newFont(typeface)
  result.size = size

proc initTypesetting*(): Typesetting =
  let typeface = readTypeface findSystemTypeface(family = "Gentium Plus")
  result.h1 = newFont(typeface, 24)
  result.h2 = newFont(typeface, 20)
  result.h3 = newFont(typeface, 16)
  result.p = newFont(typeface, 12)

proc body(xhtml: Xhtml): XmlElement =
  for e in xhtml.elements:
    if e.orKind == XmlNodeKind.`XmlElement` and e.xmlElement.name == "body":
      return e.xmlElement
  raise newException(ValueError, "no body in XHTML document")

proc name(node: XmlNode): string = node.xmlElement.name

proc text(node: XmlNode): string =
  case node.orKind
  of XmlNodeKind.XmlElement:
    for e in node.xmlElement.elements:
      result.add e.text
  of XmlNodeKind.XmlText:
    result.add node.xmlText.data

func height(arr: Arrangement): float =
  if arr.positions.len > 0: result = arr.positions[arr.positions.high].y

proc add(result: var Arrangement; other: sink Arrangement) =
  if result.lines.len == 0:
    result = other
  else:
    let runeOff = result.runes.len
    add(result.lines, map(other.lines,
      proc (x: (int, int)): (int, int) = (x[0]+runeOff, x[1]+runeOff)))
    add(result.spans, map(other.spans,
      proc (x: (int, int)): (int, int) = (x[0]+runeOff, x[1]+runeOff)))
    add(result.fonts, other.fonts)
    add(result.runes, other.runes)
    let yOff = result.positions[result.positions.high].y
    add(result.positions,
      map(other.positions,
        proc(pos: Vec2): Vec2 = vec2(pos.x, pos.y + yOff)))
    add(result.selectionRects,
      map(other.selectionRects,
        proc(rect: Rect): Rect = rect(rect.x, rect.y + yOff, rect.w, rect.h)))

proc render*(ts: Typesetting; xhtml: Xhtml): Image =
  # TODO: render by font size, not by wh
  var wh = computeBounds(ts.p, "X")
  wh.x = wh.x * 80
  wh.y = wh.y * 50
  let margin = wh / 9.0
  var
    printSpace = wh * (7.0 / 9.0)
    pages = @[Arrangement()]

  proc pageEnd(): float =
    let
      pi = pages.high
      li = pages[pi].lines.high
      ci = pages[pi].lines[li][1]
    pages[pi].positions[ci].y

  proc append(span: Span) =
    var arr = typeset(@[span], printSpace)
    if pages[pages.high].height + arr.height < printSpace.y:
      pages[pages.high].add arr
    else:
      discard

  for e in xhtml.body.elements:
    let tag = e.name
    case tag
    of "h1":
      append newSpan(e.text & "\n", ts.h1)
    of "h2":
      append newSpan("\n" & e.text & "\n", ts.h2)
    of "h3":
      append newSpan("\n" & e.text & "\n", ts.h3)
    of "p":
      append newSpan("        " & e.text & "\n\n", ts.p)
    else:
      discard
      # raise newException(ValueError, "unhandled element in XHTML :" & tag)

  result = newImage(int wh.x, int wh.y)
  fill(result, rgba(255, 255, 255, 255))
  fillText(result, pages[0], translate(margin))

M src/zua.nim => src/zua.nim +2 -2
@@ 11,8 11,8 @@ import nimsvg
import bumpy, pixie, pixie/fileformats/svg

import sdl2

import svui, svui/render/xhtml
import svui
import ./private/render/xhtml

type
  Svui = svui.Svui[Ref]