initial released
This commit is contained in:
parent
27d66b0e51
commit
0bb2a4d7fe
9 changed files with 2700 additions and 37 deletions
11
README.md
11
README.md
|
@ -4,3 +4,14 @@ another experimential typesetting tool
|
||||||
|
|
||||||
## Known issues
|
## 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}
|
{@lang|en}%
|
||||||
{@def|@font|{@quote|FreeSans|AR PL UKai TW}}
|
%{@def|@font|{@quote|FreeSans|AR PL UKai TW}}%
|
||||||
{@def|@fontsize|12}
|
%{@def|@fontsize|12}%
|
||||||
{@def|@linewidth|200} %in px%
|
%{@def|@linewidth|200} %in px
|
||||||
{@def|@spacing|{@hglue|{@ex|1}|2}}
|
%{@def|@spacing|{@hglue|{@ex|1}|2}}%
|
||||||
{@def|@cjk_spacing|{@hglue|{@ex|0}|0.001}}
|
%{@def|@cjk_spacing|{@hglue|{@ex|0}|0.001}}%
|
||||||
11111 222 3333333 444444 555555 666666 77777 88888 99999 000000 11111 22222 33333 55555
|
%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.測試一段漢字,究竟漢字會不會斷行呢
|
143
src/arrange.jl
143
src/arrange.jl
|
@ -3,35 +3,160 @@ using Match
|
||||||
|
|
||||||
u = Main.uahgi
|
u = Main.uahgi
|
||||||
|
|
||||||
# the cost of make ith to jth char a line
|
total_cost_table = Dict()
|
||||||
function cost(items, i, j, linewidth)
|
|
||||||
|
# the cost of make ith to jth item a line
|
||||||
|
function cost(items, i, j, linewidth, last_of_queue=false)
|
||||||
slice = items[i:j]
|
slice = items[i:j]
|
||||||
cost_list = map(x -> item_cost(x), slice)
|
last = items[j]
|
||||||
sum_of_cost_list = reduce((x, y)-> x+y, cost_list)
|
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
|
if sum_of_cost_list > linewidth
|
||||||
return Inf
|
return Inf
|
||||||
else
|
else
|
||||||
return linewidth - sum_of_cost_list
|
return (linewidth - sum_of_cost_list) ^ 3
|
||||||
end
|
end
|
||||||
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
|
if typeof(i) == u.HGlue
|
||||||
return i.wd
|
return i.wd
|
||||||
elseif typeof(i) == u.ChBox
|
elseif typeof(i) == u.ChBox
|
||||||
return i.wd
|
return i.wd
|
||||||
|
elseif typeof(i) == u.Disc
|
||||||
|
return item_width(i.orig)
|
||||||
else
|
else
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function arrange(vbox, env)
|
function arrange(vbox, env)
|
||||||
result_vbox_inner = []
|
|
||||||
|
result_vbox_inner = [u.HBox([],nothing,nothing,nothing,nothing,nothing)]
|
||||||
linewidth = parse(Int, env["linewidth"])
|
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
|
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
|
||||||
|
|
||||||
|
|
||||||
|
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
|
end
|
|
@ -89,9 +89,9 @@ function match_char(ast_item, patterns)
|
||||||
for i in splitted
|
for i in splitted
|
||||||
push!(final, c.CHAR(i))
|
push!(final, c.CHAR(i))
|
||||||
push!(final, c.SEQ([c.ELE([c.ID("disc")]),
|
push!(final, c.SEQ([c.ELE([c.ID("disc")]),
|
||||||
|
c.ELE([c.CHAR("-")]),
|
||||||
c.ELE([]),
|
c.ELE([]),
|
||||||
c.ELE([]),
|
c.ELE([])]))
|
||||||
c.ELE([c.CHAR("-")])]))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
final = final[1:end-1]
|
final = final[1:end-1]
|
||||||
|
|
|
@ -33,6 +33,11 @@ interp: the intepreter of the uahgi.
|
||||||
function interp(ast, env, res_box, put_char=true_)
|
function interp(ast, env, res_box, put_char=true_)
|
||||||
#println("INTERP", ast)
|
#println("INTERP", ast)
|
||||||
@match ast begin
|
@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.SEQ([c.ELE([c.ID("def")]),
|
||||||
c.ELE([c.ID(id)]),val]) =>
|
c.ELE([c.ID(id)]),val]) =>
|
||||||
begin
|
begin
|
||||||
|
@ -87,7 +92,7 @@ function interp(ast, env, res_box, put_char=true_)
|
||||||
font_size)
|
font_size)
|
||||||
ex_in_px = charmetrics.wd
|
ex_in_px = charmetrics.wd
|
||||||
(val, _, _) = interp(val, env, res_box, false_)
|
(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)
|
return (val_ex_in_px, env, res_box)
|
||||||
end
|
end
|
||||||
c.SEQ([c.ELE([c.ID("hglue")]),
|
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_)
|
(width_evaled, _, _) = interp(width, env, res_box, false_)
|
||||||
(stretch_evaled, _, _) = interp(stretch, env, res_box, false_)
|
(stretch_evaled, _, _) = interp(stretch, env, res_box, false_)
|
||||||
push!(res_box.eles[end].eles,
|
push!(res_box.eles[end].eles,
|
||||||
u.HGlue(width_evaled, parse(Int, stretch_evaled))
|
u.HGlue(width_evaled, parse(Float64, stretch_evaled))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -145,7 +150,6 @@ function interp(ast, env, res_box, put_char=true_)
|
||||||
(after_evaled, _, _) = interp(after_item, env, res_box, gen_chbox_)
|
(after_evaled, _, _) = interp(after_item, env, res_box, gen_chbox_)
|
||||||
(orig_evaled, _, _) = interp(orig_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)
|
ret = u.Disc(before_evaled, after_evaled, orig_evaled)
|
||||||
println("RETURN", ret)
|
|
||||||
push!(res_box.eles[end].eles, ret)
|
push!(res_box.eles[end].eles, ret)
|
||||||
return (ret, env, res_box)
|
return (ret, env, res_box)
|
||||||
|
|
||||||
|
@ -169,8 +173,8 @@ function interp(ast, env, res_box, put_char=true_)
|
||||||
# empty item
|
# empty item
|
||||||
[] => return (ast, env, res_box)
|
[] => return (ast, env, res_box)
|
||||||
_ => begin
|
_ => begin
|
||||||
println("不知道")
|
println("unknown token", ast)
|
||||||
val_evaled = "不知道"
|
val_evaled = nothing
|
||||||
return (val_evaled, env, res_box)
|
return (val_evaled, env, res_box)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -45,9 +45,7 @@ part = seq | comment | space | newline | id | char
|
||||||
all = (Repeat(part) + Eos()) |> Passes.Classes.PROG
|
all = (Repeat(part) + Eos()) |> Passes.Classes.PROG
|
||||||
|
|
||||||
function parse(input)
|
function parse(input)
|
||||||
print(input)
|
|
||||||
ast = parse_one(input, all)[1]
|
ast = parse_one(input, all)[1]
|
||||||
print("\n" * string(ast) * "\n")
|
|
||||||
|
|
||||||
|
|
||||||
#print(parse_one(, Pattern(r".b.")))
|
#print(parse_one(, Pattern(r".b.")))
|
||||||
|
@ -63,7 +61,6 @@ function parse(input)
|
||||||
end
|
end
|
||||||
|
|
||||||
new_ast = Passes.Classes.PROG(ast_val)
|
new_ast = Passes.Classes.PROG(ast_val)
|
||||||
print(ast_to_string(new_ast))
|
|
||||||
return new_ast
|
return new_ast
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -101,11 +101,11 @@ function check_char_size(char, font_path, font_size, font_index=0)
|
||||||
|
|
||||||
metrics = glyphrec.metrics
|
metrics = glyphrec.metrics
|
||||||
# horizonal mode
|
# horizonal mode
|
||||||
width = metricToPx(metrics.horiAdvance, font_size)
|
width = metricToPx(metrics.horiAdvance, font_size) * 0.75
|
||||||
#from baseline up to top of the glyph
|
#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
|
#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)
|
return CharMetrics(height, depth, width)
|
||||||
|
@ -131,5 +131,15 @@ end
|
||||||
# '安',
|
# '安',
|
||||||
# default_font_path,
|
# default_font_path,
|
||||||
# 20)
|
# 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("parsing.jl")
|
||||||
include("interp.jl")
|
include("interp.jl")
|
||||||
include("arrange.jl")
|
include("arrange.jl")
|
||||||
|
include("pdfoperating.jl")
|
||||||
|
|
||||||
using .Interp
|
using .Interp
|
||||||
using .Parsing
|
using .Parsing
|
||||||
using .Arrange
|
using .Arrange
|
||||||
|
using .PDFOperating
|
||||||
using ArgParse
|
using ArgParse
|
||||||
|
|
||||||
export ChBox, HGlue
|
export ChBox, HGlue
|
||||||
|
@ -136,12 +138,9 @@ function main()
|
||||||
output_box_result = Interp.interp_main(ast, default_env, output_box_orig)
|
output_box_result = Interp.interp_main(ast, default_env, output_box_orig)
|
||||||
env = output_box_result[2]
|
env = output_box_result[2]
|
||||||
output_box = output_box_result[3]
|
output_box = output_box_result[3]
|
||||||
print(output_box)
|
|
||||||
#TODO
|
|
||||||
arranged = Arrange.arrange(output_box, env)
|
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
|
end
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
Loading…
Reference in a new issue