initial released
This commit is contained in:
parent
27d66b0e51
commit
0bb2a4d7fe
9 changed files with 2700 additions and 37 deletions
13
README.md
13
README.md
|
@ -3,4 +3,15 @@
|
|||
another experimential typesetting tool
|
||||
|
||||
## Known issues
|
||||
- Some .ttc file(eg. Noto Sans/Serif CJK) can't be loaded. It seems to be a libharu bug. For those want to use Noto Sans/Serif CJK, please download the .ttf file on Google Fonts. [Example (Trad. Chinese Version)](https://fonts.google.com/noto/specimen/Noto+Sans+TC)
|
||||
- Some .ttc file(eg. Noto Sans/Serif CJK) can't be loaded. It seems to be a libharu bug. For those want to use Noto Sans/Serif CJK, please download the .ttf file on Google Fonts. [Example (Trad. Chinese Version)](https://fonts.google.com/noto/specimen/Noto+Sans+TC)
|
||||
|
||||
## How to test
|
||||
To test with the example, please using the command:
|
||||
|
||||
```
|
||||
cd /path/to/uahgi
|
||||
julia --project="." src/uahgi.jl example/ex1.ug
|
||||
```
|
||||
|
||||
## Origin of the name
|
||||
The name is after the Taiwanese/Hokkien word for the movable type, which is ua̍h-gī (活字), in Taichung-Basin accent.
|
2516
example/ex1.pdf
Normal file
2516
example/ex1.pdf
Normal file
File diff suppressed because one or more lines are too long
|
@ -1,7 +1,8 @@
|
|||
{@lang|en}
|
||||
{@def|@font|{@quote|FreeSans|AR PL UKai TW}}
|
||||
{@def|@fontsize|12}
|
||||
{@def|@linewidth|200} %in px%
|
||||
{@def|@spacing|{@hglue|{@ex|1}|2}}
|
||||
{@def|@cjk_spacing|{@hglue|{@ex|0}|0.001}}
|
||||
11111 222 3333333 444444 555555 666666 77777 88888 99999 000000 11111 22222 33333 55555
|
||||
{@lang|en}%
|
||||
%{@def|@font|{@quote|FreeSans|AR PL UKai TW}}%
|
||||
%{@def|@fontsize|12}%
|
||||
%{@def|@linewidth|200} %in px
|
||||
%{@def|@spacing|{@hglue|{@ex|1}|2}}%
|
||||
%{@def|@cjk_spacing|{@hglue|{@ex|0}|0.001}}%
|
||||
%the processing of typesetting technology is so sophisticated that many people are afraid of
|
||||
the implementation of typesetting language. However, it is good to give TEXBook a try.測試一段漢字,究竟漢字會不會斷行呢
|
145
src/arrange.jl
145
src/arrange.jl
|
@ -3,35 +3,160 @@ using Match
|
|||
|
||||
u = Main.uahgi
|
||||
|
||||
# the cost of make ith to jth char a line
|
||||
function cost(items, i, j, linewidth)
|
||||
total_cost_table = Dict()
|
||||
|
||||
# the cost of make ith to jth item a line
|
||||
function cost(items, i, j, linewidth, last_of_queue=false)
|
||||
slice = items[i:j]
|
||||
cost_list = map(x -> item_cost(x), slice)
|
||||
sum_of_cost_list = reduce((x, y)-> x+y, cost_list)
|
||||
last = items[j]
|
||||
if typeof(last) == u.ChBox && last_of_queue === false
|
||||
return Inf
|
||||
end
|
||||
sum_of_cost_list = 0
|
||||
|
||||
# add disc (after) if it exists
|
||||
|
||||
if i > 2 && typeof(items[i-1]) == u.Disc
|
||||
previous = items[i-1]
|
||||
sum_of_cost_list += item_width(previous.after)
|
||||
end
|
||||
|
||||
if typeof(last) != u.Disc
|
||||
cost_list = map(x -> item_width(x), slice)
|
||||
sum_of_cost_list += reduce((x, y)-> x+y, cost_list) - item_width(slice[end])
|
||||
else
|
||||
cost_list = map(x -> item_width(x), slice[1:end-1])
|
||||
if cost_list != []
|
||||
sum_of_cost_list += reduce((x, y)-> x+y, cost_list)
|
||||
end
|
||||
last_width = item_width(last.before)
|
||||
sum_of_cost_list += last_width
|
||||
end
|
||||
if sum_of_cost_list > linewidth
|
||||
return Inf
|
||||
else
|
||||
return linewidth - sum_of_cost_list
|
||||
return (linewidth - sum_of_cost_list) ^ 3
|
||||
end
|
||||
end
|
||||
|
||||
function item_cost(i)
|
||||
"""the total cost from 1 to n"""
|
||||
function total_cost(items, n, linewidth, last_of_queue=false)
|
||||
if haskey(total_cost_table, n)
|
||||
return total_cost_table[n][1]
|
||||
end
|
||||
# first to nth item cost
|
||||
fst_to_nth_cost = cost(items, 1, n, linewidth, last_of_queue)
|
||||
|
||||
if fst_to_nth_cost < Inf
|
||||
total_cost_table[n] = [fst_to_nth_cost, 0]
|
||||
return fst_to_nth_cost
|
||||
else
|
||||
mininal_cost = +Inf
|
||||
prev_breakpoint = nothing
|
||||
for j in 1:1:(n-1)
|
||||
tmp_cost = total_cost(items, j, linewidth, false) + cost(items, j+1, n, linewidth, last_of_queue)
|
||||
if tmp_cost < mininal_cost
|
||||
mininal_cost = tmp_cost
|
||||
prev_breakpoint = j
|
||||
end
|
||||
end
|
||||
|
||||
total_cost_table[n] = [mininal_cost, prev_breakpoint]
|
||||
return mininal_cost
|
||||
end
|
||||
end
|
||||
|
||||
"""width of a item"""
|
||||
function item_width(i)
|
||||
if typeof(i) == u.HGlue
|
||||
return i.wd
|
||||
elseif typeof(i) == u.ChBox
|
||||
return i.wd
|
||||
elseif typeof(i) == u.Disc
|
||||
return item_width(i.orig)
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
function arrange(vbox, env)
|
||||
result_vbox_inner = []
|
||||
|
||||
result_vbox_inner = [u.HBox([],nothing,nothing,nothing,nothing,nothing)]
|
||||
linewidth = parse(Int, env["linewidth"])
|
||||
print(cost(vbox.eles[1].eles, 1, 20, linewidth))
|
||||
eles = vbox.eles[1].eles
|
||||
total_cost(eles, length(eles), linewidth, true)
|
||||
|
||||
return vbox
|
||||
|
||||
current_point = length(eles)
|
||||
breakpoint_list = [current_point]
|
||||
while total_cost_table[current_point][2] !== 0.0
|
||||
current_point = total_cost_table[current_point][2]
|
||||
push!(breakpoint_list, current_point)
|
||||
end
|
||||
|
||||
breakpoint_list_reversed = reverse(breakpoint_list)
|
||||
for (x, i) in enumerate(1:1:length(eles))
|
||||
item = eles[x]
|
||||
if !(x in breakpoint_list_reversed)
|
||||
if typeof(item) == u.Disc
|
||||
if item.orig != []
|
||||
push!(result_vbox_inner[end].eles, item.orig)
|
||||
end
|
||||
else
|
||||
push!(result_vbox_inner[end].eles, item)
|
||||
end
|
||||
# x is the last one
|
||||
elseif i == length(eles)
|
||||
push!(result_vbox_inner[end].eles, item)
|
||||
# x in breakpoint_list_reversed
|
||||
else
|
||||
if typeof(item) == u.Disc
|
||||
if item.before != []
|
||||
push!(result_vbox_inner[end].eles, item.before)
|
||||
end
|
||||
push!(result_vbox_inner, u.HBox([],nothing,nothing,nothing,nothing,nothing))
|
||||
if item.after != []
|
||||
push!(result_vbox_inner[end].eles, item.after)
|
||||
end
|
||||
else
|
||||
push!(result_vbox_inner, u.HBox([],nothing,nothing,nothing,nothing,nothing))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return result_vbox_inner
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function position(box_inner, env#=to be used later=#)
|
||||
pages = [] #a subarray is the content of a page
|
||||
orig_y = 700 # it can be derived from env
|
||||
orig_x = 100 # it can be derived from env
|
||||
posX = orig_x #cursor x
|
||||
posY = orig_y #cursor y
|
||||
baselineskip = 30 # it can be derived from env
|
||||
for hbox in box_inner
|
||||
pages = position_chbox(hbox.eles, pages, posX, posY)
|
||||
posX = orig_x
|
||||
posY -= baselineskip
|
||||
end
|
||||
return pages
|
||||
end
|
||||
|
||||
"""positioning all the chboxes in a HBox"""
|
||||
function position_chbox(hbox_eles, pages, posX, posY)
|
||||
for i in hbox_eles
|
||||
if typeof(i) == u.HGlue
|
||||
deltaX = i.wd
|
||||
posX += deltaX
|
||||
else #ChBox
|
||||
deltaX = i.wd
|
||||
i.x = posX
|
||||
i.y = posY
|
||||
push!(pages, i)
|
||||
posX += deltaX
|
||||
end
|
||||
end
|
||||
return pages
|
||||
end
|
||||
end
|
||||
|
|
|
@ -89,9 +89,9 @@ function match_char(ast_item, patterns)
|
|||
for i in splitted
|
||||
push!(final, c.CHAR(i))
|
||||
push!(final, c.SEQ([c.ELE([c.ID("disc")]),
|
||||
c.ELE([c.CHAR("-")]),
|
||||
c.ELE([]),
|
||||
c.ELE([]),
|
||||
c.ELE([c.CHAR("-")])]))
|
||||
c.ELE([])]))
|
||||
end
|
||||
|
||||
final = final[1:end-1]
|
||||
|
|
|
@ -33,6 +33,11 @@ interp: the intepreter of the uahgi.
|
|||
function interp(ast, env, res_box, put_char=true_)
|
||||
#println("INTERP", ast)
|
||||
@match ast begin
|
||||
c.ID(id) =>
|
||||
begin
|
||||
print("ID____", id)
|
||||
return interp(env[id], env, res_box, true_)
|
||||
end
|
||||
c.SEQ([c.ELE([c.ID("def")]),
|
||||
c.ELE([c.ID(id)]),val]) =>
|
||||
begin
|
||||
|
@ -87,7 +92,7 @@ function interp(ast, env, res_box, put_char=true_)
|
|||
font_size)
|
||||
ex_in_px = charmetrics.wd
|
||||
(val, _, _) = interp(val, env, res_box, false_)
|
||||
val_ex_in_px = ex_in_px * parse(Int,val)
|
||||
val_ex_in_px = ex_in_px * parse(Float64,val)
|
||||
return (val_ex_in_px, env, res_box)
|
||||
end
|
||||
c.SEQ([c.ELE([c.ID("hglue")]),
|
||||
|
@ -96,7 +101,7 @@ function interp(ast, env, res_box, put_char=true_)
|
|||
(width_evaled, _, _) = interp(width, env, res_box, false_)
|
||||
(stretch_evaled, _, _) = interp(stretch, env, res_box, false_)
|
||||
push!(res_box.eles[end].eles,
|
||||
u.HGlue(width_evaled, parse(Int, stretch_evaled))
|
||||
u.HGlue(width_evaled, parse(Float64, stretch_evaled))
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -145,7 +150,6 @@ function interp(ast, env, res_box, put_char=true_)
|
|||
(after_evaled, _, _) = interp(after_item, env, res_box, gen_chbox_)
|
||||
(orig_evaled, _, _) = interp(orig_item, env, res_box, gen_chbox_)
|
||||
ret = u.Disc(before_evaled, after_evaled, orig_evaled)
|
||||
println("RETURN", ret)
|
||||
push!(res_box.eles[end].eles, ret)
|
||||
return (ret, env, res_box)
|
||||
|
||||
|
@ -169,8 +173,8 @@ function interp(ast, env, res_box, put_char=true_)
|
|||
# empty item
|
||||
[] => return (ast, env, res_box)
|
||||
_ => begin
|
||||
println("不知道")
|
||||
val_evaled = "不知道"
|
||||
println("unknown token", ast)
|
||||
val_evaled = nothing
|
||||
return (val_evaled, env, res_box)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -45,9 +45,7 @@ part = seq | comment | space | newline | id | char
|
|||
all = (Repeat(part) + Eos()) |> Passes.Classes.PROG
|
||||
|
||||
function parse(input)
|
||||
print(input)
|
||||
ast = parse_one(input, all)[1]
|
||||
print("\n" * string(ast) * "\n")
|
||||
|
||||
|
||||
#print(parse_one(, Pattern(r".b.")))
|
||||
|
@ -63,7 +61,6 @@ function parse(input)
|
|||
end
|
||||
|
||||
new_ast = Passes.Classes.PROG(ast_val)
|
||||
print(ast_to_string(new_ast))
|
||||
return new_ast
|
||||
end
|
||||
|
||||
|
|
|
@ -101,11 +101,11 @@ function check_char_size(char, font_path, font_size, font_index=0)
|
|||
|
||||
metrics = glyphrec.metrics
|
||||
# horizonal mode
|
||||
width = metricToPx(metrics.horiAdvance, font_size)
|
||||
width = metricToPx(metrics.horiAdvance, font_size) * 0.75
|
||||
#from baseline up to top of the glyph
|
||||
height = metricToPx(metrics.horiBearingY, font_size)
|
||||
height = metricToPx(metrics.horiBearingY, font_size) * 0.75
|
||||
#from baseline down to the bottom of the glyph
|
||||
depth = metricToPx(metrics.height - metrics.horiBearingY, font_size)
|
||||
depth = metricToPx(metrics.height - metrics.horiBearingY, font_size) * 0.75
|
||||
|
||||
|
||||
return CharMetrics(height, depth, width)
|
||||
|
@ -131,5 +131,15 @@ end
|
|||
# '安',
|
||||
# default_font_path,
|
||||
# 20)
|
||||
"""generate_pdf"""
|
||||
function generate_pdf(box_list, file_path)
|
||||
pdf = createPDF()
|
||||
page = addPage(pdf)
|
||||
for ch in box_list
|
||||
font = load_font(pdf, ch.font_path)
|
||||
put_text(page, ch.char, font, ch.size, ch.x, ch.y)
|
||||
end
|
||||
save_pdf(pdf, file_path)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,10 +2,12 @@ module uahgi
|
|||
include("parsing.jl")
|
||||
include("interp.jl")
|
||||
include("arrange.jl")
|
||||
include("pdfoperating.jl")
|
||||
|
||||
using .Interp
|
||||
using .Parsing
|
||||
using .Arrange
|
||||
using .PDFOperating
|
||||
using ArgParse
|
||||
|
||||
export ChBox, HGlue
|
||||
|
@ -136,12 +138,9 @@ function main()
|
|||
output_box_result = Interp.interp_main(ast, default_env, output_box_orig)
|
||||
env = output_box_result[2]
|
||||
output_box = output_box_result[3]
|
||||
print(output_box)
|
||||
#TODO
|
||||
arranged = Arrange.arrange(output_box, env)
|
||||
|
||||
|
||||
|
||||
unit_positioned = Arrange.position(arranged, env)
|
||||
generated_pdf = PDFOperating.generate_pdf(unit_positioned, file_path[1:end-3] * ".pdf")
|
||||
end
|
||||
|
||||
main()
|
||||
|
|
Loading…
Reference in a new issue