% language=uk

\environment luatex-style

\startcomponent luatex-harfbuzz

\definelayer
  [hbpage]
  [width=\paperwidth,
   height=\paperheight]


\setupbackgrounds
  [page]
  [background=hbpage,
   state=start]


\startchapter[reference=harfbuzz,title={The HarfBuzz libraries}]


\startsection[title={The \type {luaharfbuzz} library}][library=luaharfbuzz]

The documentation of the library is a raw "html-to-pdf" 
rendering of the API at \type{https://harfbuzz.github.io/luaharfbuzz}; 
the  example is almost the verbatim copy \type{core_type.lua}.
\page


\dorecurse{15}{%
\setlayer[hbpage][x=0mm,y=0mm,]{\externalfigure[graphics/luaharfbuzz-Documentation.pdf][page=\recurselevel,scale=950]}
\blank
\page[empty]
}

\dorecurse{3}{%
\setlayer[hbpage][x=0mm,y=0mm,]{\externalfigure[graphics/luaharfbuzz-Documentation-subset.pdf][page=\recurselevel,scale=950]}
\blank
\page[empty]
}


\startsubsection[title={Example}]

The example is (a small modification of)  the file \type{core_types.lua}
and it requires the file \type{harfbuzz.lua}; both are 
under \type{luaharfbuzz}  folder of the source code.
The fonts \type{notonastaliq.ttf} and \type{amiri-regular.ttf'} are under 
\type{luaharfbuzz/fonts}.


\blank[3*big]

\startbuffer[hbmod]
local hb = require("luaharfbuzz")

-- special tags
hb.Tag.NONE = hb.Tag.new()

-- special script codes (ISO 15924)
hb.Script.COMMON    = hb.Script.new("Zyyy")
hb.Script.INHERITED = hb.Script.new("Zinh")
hb.Script.UNKNOWN   = hb.Script.new("Zzzz")
hb.Script.INVALID   = hb.Script.from_iso15924_tag(hb.Tag.NONE)

-- directions
hb.Direction.INVALID = hb.Direction.new("invalid")
hb.Direction.LTR = hb.Direction.new("ltr")
hb.Direction.RTL = hb.Direction.new("rtl")
hb.Direction.TTB = hb.Direction.new("ttb")
hb.Direction.BTT = hb.Direction.new("btt")

-- special languages
hb.Language.INVALID = hb.Language.new()

hb.shape = function(font, buf, options)
  options = options or { }

  -- Apply options to buffer if they are set.
  if options.language then buf:set_language(options.language) end
  if options.script then buf:set_script(options.script) end
  if options.direction then buf:set_direction(options.direction) end

  -- Guess segment properties, in case all steps above have failed
  -- to set the right properties.
  buf:guess_segment_properties()

  local features = {}

  -- Parse features
  if type(options.features) == "string" then
    for fs in string.gmatch(options.features, '([^,]+)') do
      local feature = hb.Feature.new(fs)
      if feature then
        table.insert(features, hb.Feature.new(fs))
      else
        error(string.format("Invalid feature string: '%s'", fs))
      end
    end
  elseif type(options.features) == "table" then
    features = options.features
  elseif options.features then -- non-nil but not a string or table
    error("Invalid features option")
  end

  return hb.shape_full(font,buf,features,options.shapers or {})
end

-- For backward compatibility
hb.Buffer.HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES  = hb.Buffer.CLUSTER_LEVEL_MONOTONE_GRAPHEMES
hb.Buffer.HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS = hb.Buffer.CLUSTER_LEVEL_MONOTONE_CHARACTERS
hb.Buffer.HB_BUFFER_CLUSTER_LEVEL_CHARACTERS          = hb.Buffer.CLUSTER_LEVEL_CHARACTERS
hb.Buffer.HB_BUFFER_CLUSTER_LEVEL_DEFAULT             = hb.Buffer.CLUSTER_LEVEL_DEFAULT

hb.Tag.HB_TAG_NONE = hb.Tag.NONE

hb.Script.HB_SCRIPT_COMMON    = hb.Script.COMMON
hb.Script.HB_SCRIPT_INHERITED = hb.Script.INHERITED
hb.Script.HB_SCRIPT_UNKNOWN   = hb.Script.UNKNOWN
hb.Script.HB_SCRIPT_INVALID   = hb.Script.INVALID

hb.Language.HB_LANGUAGE_INVALID = hb.Language.INVALID

hb.Direction.HB_DIRECTION_INVALID = hb.Direction.INVALID
hb.Direction.HB_DIRECTION_LTR = hb.Direction.LTR
hb.Direction.HB_DIRECTION_RTL = hb.Direction.RTL
hb.Direction.HB_DIRECTION_TTB = hb.Direction.TTB
hb.Direction.HB_DIRECTION_BTT = hb.Direction.BTT

hb.Direction.HB_DIRECTION_IS_VALID = hb.Direction.is_valid
hb.Direction.HB_DIRECTION_IS_HORIZONTAL = hb.Direction.is_horizontal
hb.Direction.HB_DIRECTION_IS_VERTICAL = hb.Direction.is_vertical
hb.Direction.HB_DIRECTION_IS_FORWARD = hb.Direction.is_forward
hb.Direction.HB_DIRECTION_IS_BACKWARD = hb.Direction.is_backward

hb.Buffer.get_glyph_infos_and_positions = hb.Buffer.get_glyphs

return hb

\stopbuffer

\blank[3*big]

The file \type{harfbuzz.lua}:
\typebuffer[hbmod]


\startbuffer[hbex]
local harfbuzz = require('harfbuzz')

-- Harfbuzz API Version
print("Harfbuzz API version", harfbuzz.version())

-- Shapers available
print("Shapers:", harfbuzz.shapers())


-- harfbuzz.Face
local face = harfbuzz.Face.new('notonastaliq.ttf')
print('\nFace upem = '..face:get_upem())

-- harfbuzz.Font
local font = harfbuzz.Font.new(face)
local xs, xy = font:get_scale()
print("\nDefault font scale = X: "..xs..", Y: "..xy)

-- harfbuzz.Buffer
local text = "یہ" -- U+06CC U+06C1
local buf = harfbuzz.Buffer.new()
buf:add_utf8(text)

-- harfbuzz.shape (Shapes text)
print("\nShaping '"..text.."' set with Noto Nastaliq Urdu")
harfbuzz.shape(font, buf, { language = harfbuzz.Language.new("urd"), 
script = harfbuzz.Script.new("Arab"), direction = harfbuzz.Direction.RTL})

local glyphs = buf:get_glyphs()
print("No. of glyphs", #glyphs)
for k,v in pairs(glyphs) do 
  print(k) 
  for k1,v1 in pairs(v) do 
   print("",k1,v1) 
  end 
end 

local opts = { language = harfbuzz.Language.new("eng"), 
script = harfbuzz.Script.new("Latn"), direction = harfbuzz.Direction.LTR }

local amiri_face = harfbuzz.Face.new('amiri-regular.ttf')
local amiri_font = harfbuzz.Font.new(amiri_face)

-- shaping '123' w/o features
print("\nShaping '123' set with Amiri Regular and no features")
buf= harfbuzz.Buffer.new()
buf:add_utf8("123")
harfbuzz.shape(amiri_font, buf, opts)
glyphs = buf:get_glyphs()
for k,v in pairs(glyphs) do 
  print(k) 
  for k1,v1 in pairs(v) do 
   print("",k1,v1) 
  end 
end 


-- shaping '123' with '+numr' (numerators)
print("\nShaping '123' set with Amiri Regular with 'numr' feature turned on")
buf= harfbuzz.Buffer.new()
buf:add_utf8("123")
opts.features = "+numr"
harfbuzz.shape(amiri_font, buf, opts)
glyphs = buf:get_glyphs()
for k,v in pairs(glyphs) do 
  print(k) 
  for k1,v1 in pairs(v) do 
   print("",k1,v1) 
  end 
end 
\stopbuffer

The example:
\typebuffer[hbex]


\startbuffer[hbout]

Harfbuzz API version	12.2.0
Shapers:	graphite2	ot	fallback

Face upem = 2048

Default font scale = X: 2048, Y: 2048

Shaping 'یہ' set with Noto Nastaliq Urdu
No. of glyphs	4
1
	codepoint	277
	y_advance	0.0
	cluster	2
	flags	1.0
	y_offset	0.0
	x_advance	472.0
	x_offset	0.0
2
	codepoint	19
	y_advance	0.0
	cluster	0
	y_offset	-383.0
	x_advance	0.0
	x_offset	310.0
3
	codepoint	985
	y_advance	0.0
	cluster	0
	y_offset	0.0
	x_advance	0.0
	x_offset	0.0
4
	codepoint	316
	y_advance	0.0
	cluster	0
	y_offset	-68.0
	x_advance	731.0
	x_offset	0.0

Shaping '123' set with Amiri Regular and no features
1
	codepoint	20
	y_advance	0.0
	cluster	0
	y_offset	0.0
	x_advance	1090.0
	x_offset	0.0
2
	codepoint	21
	y_advance	0.0
	cluster	1
	y_offset	0.0
	x_advance	1090.0
	x_offset	0.0
3
	codepoint	22
	y_advance	0.0
	cluster	2
	y_offset	0.0
	x_advance	1090.0
	x_offset	0.0

Shaping '123' set with Amiri Regular with 'numr' feature turned on
1
	codepoint	6673
	y_advance	0.0
	cluster	0
	y_offset	0.0
	x_advance	600.0
	x_offset	0.0
2
	codepoint	6674
	y_advance	0.0
	cluster	1
	y_offset	0.0
	x_advance	600.0
	x_offset	0.0
3
	codepoint	6675
	y_advance	0.0
	cluster	2
	y_offset	0.0
	x_advance	600.0
	x_offset	0.0
\stopbuffer

\blank[3*big]
The result:
\typebuffer[hbout]


\stopsubsection
\stopsection

\stopchapter
\stopcomponent
