├── examples ├── .gitignore ├── data │ ├── v.jpg │ ├── Graduate-Regular.ttf │ └── ShelleyAllegro.ttf ├── example_06_simple_pdf.v ├── example_07_jpeg.v ├── example_09_simple_ttf_font.v ├── example_01_raw_pdf.v ├── example_05_simple_book.v ├── example_02_raw_pages.v ├── example_03_two_columns.v ├── example_08_ttf_font.v └── example_04_lin_gradient.v ├── v.mod ├── .gitattributes ├── .gitignore ├── LICENSE ├── ttf_font.v ├── README.md ├── pdf.v └── afm_metrics.v /examples/.gitignore: -------------------------------------------------------------------------------- 1 | *.pdf -------------------------------------------------------------------------------- /examples/data/v.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlang/pdf/HEAD/examples/data/v.jpg -------------------------------------------------------------------------------- /examples/data/Graduate-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlang/pdf/HEAD/examples/data/Graduate-Regular.ttf -------------------------------------------------------------------------------- /examples/data/ShelleyAllegro.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlang/pdf/HEAD/examples/data/ShelleyAllegro.ttf -------------------------------------------------------------------------------- /v.mod: -------------------------------------------------------------------------------- 1 | Module { 2 | name: 'pdf', 3 | description: 'Light PDF writer.', 4 | version: '0.0.1' 5 | repo_url: 'https://github.com/vlang/pdf', 6 | dependencies: [] 7 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.v linguist-language=V text=auto eol=lf 2 | *.vv linguist-language=V text=auto eol=lf 3 | *.vsh linguist-language=V text=auto eol=lf 4 | **/v.mod linguist-language=V text=auto eol=lf 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | examples/example_01_raw_pdf 2 | examples/example_02_raw_pages 3 | examples/example_03_two_columns 4 | examples/example_04_lin_gradient 5 | examples/example_05_simple_book 6 | examples/example_06_simple_pdf 7 | examples/example_07_jpeg 8 | examples/example_08_ttf_font 9 | examples/example_09_simple_ttf_font 10 | -------------------------------------------------------------------------------- /examples/example_06_simple_pdf.v: -------------------------------------------------------------------------------- 1 | import pdf 2 | import os 3 | 4 | fn main() { 5 | mut doc := pdf.Pdf{} 6 | doc.init() 7 | 8 | page_n := doc.create_page(pdf.Page_params{ 9 | format: 'A4' 10 | gen_content_obj: true 11 | compress: false 12 | }) 13 | mut page := &doc.page_list[page_n] 14 | page.user_unit = pdf.mm_unit 15 | 16 | mut fnt_params := pdf.Text_params{ 17 | font_size: 22.0 18 | font_name: 'Helvetica' 19 | s_color: pdf.RGB{ 20 | r: 0 21 | g: 0 22 | b: 0 23 | } 24 | f_color: pdf.RGB{ 25 | r: 0 26 | g: 0 27 | b: 0 28 | } 29 | } 30 | 31 | // Declare the base (Type1 font) we want use 32 | if !doc.use_base_font(fnt_params.font_name) { 33 | eprintln('ERROR: Font ${fnt_params.font_name} not available!') 34 | return 35 | } 36 | 37 | // write the string 38 | page.push_content(page.draw_base_text('My first string.', 10, 10, fnt_params)) 39 | 40 | // render the PDF 41 | txt := doc.render()! 42 | 43 | // write it to a file 44 | os.write_file_array('example06.pdf', txt)! 45 | } 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2020 Dario Deledda 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/example_07_jpeg.v: -------------------------------------------------------------------------------- 1 | import pdf 2 | import os 3 | 4 | fn main() { 5 | mut doc := pdf.Pdf{} 6 | doc.init() 7 | 8 | page_n := doc.create_page(pdf.Page_params{ 9 | format: 'A4' 10 | gen_content_obj: true 11 | compress: false 12 | }) 13 | mut page := &doc.page_list[page_n] 14 | page.user_unit = pdf.mm_unit 15 | 16 | mut fnt_params := pdf.Text_params{ 17 | font_size: 22.0 18 | font_name: 'Helvetica' 19 | s_color: pdf.RGB{ 20 | r: 0 21 | g: 0 22 | b: 0 23 | } 24 | f_color: pdf.RGB{ 25 | r: 0 26 | g: 0 27 | b: 0 28 | } 29 | } 30 | 31 | // Declare the base (Type1 font) we want use 32 | if !doc.use_base_font(fnt_params.font_name) { 33 | eprintln('ERROR: Font ${fnt_params.font_name} not available!') 34 | return 35 | } 36 | 37 | // write the string 38 | page.push_content(page.draw_base_text('My first string.', 10, 10, fnt_params)) 39 | 40 | // read a jpeg image from the disk 41 | jpeg_data := os.read_bytes('data/v.jpg') or { panic(err) } 42 | jpeg_id := doc.add_jpeg_resource(jpeg_data) 43 | // tell the page we want use a this jpeg in the page 44 | page.use_jpeg(jpeg_id) 45 | 46 | // get width and height in pixel of the jpeg image 47 | _, w, h := pdf.get_jpeg_info(jpeg_data) 48 | h_scale := h / w 49 | 50 | page.push_content(page.draw_jpeg(jpeg_id, pdf.Box{ 51 | x: 10 52 | y: 60 53 | w: 30 54 | h: 30 * h_scale 55 | })) 56 | 57 | // render the PDF 58 | txt := doc.render()! 59 | 60 | // write it to a file 61 | os.write_file_array('example07.pdf', txt)! 62 | } 63 | -------------------------------------------------------------------------------- /examples/example_09_simple_ttf_font.v: -------------------------------------------------------------------------------- 1 | import pdf 2 | import os 3 | 4 | fn main() { 5 | mut doc := pdf.Pdf{} 6 | doc.init() 7 | 8 | // add TTF font obj 9 | font_name := 'GR' 10 | doc.load_ttf_font('data/Graduate-Regular.ttf', font_name, 1.0) 11 | 12 | page_n := doc.create_page(pdf.Page_params{ 13 | format: 'A4' 14 | gen_content_obj: true 15 | compress: false 16 | }) 17 | mut page := &doc.page_list[page_n] 18 | page.user_unit = pdf.mm_unit 19 | 20 | mut fnt_params := pdf.Text_params{ 21 | font_size: 22.0 22 | font_name: font_name 23 | s_color: pdf.RGB{ 24 | r: 0 25 | g: 0 26 | b: 0 27 | } 28 | f_color: pdf.RGB{ 29 | r: 0 30 | g: 0 31 | b: 0 32 | } 33 | } 34 | 35 | // Declare the base (Type1 font) we want use 36 | if fnt_params.font_name != font_name && !doc.use_base_font(fnt_params.font_name) { 37 | eprintln('ERROR: Font ${fnt_params.font_name} not available!') 38 | return 39 | } 40 | 41 | // write the string plus € symbol using an octal codepoint 42 | // for the font Graduate-Regular.ttf in the data folder 43 | // that use the ISO-8859-1 Latin1 codification of chars 44 | // € is 0x80 or 0o200 or 128 in decimal. 45 | // In PDF you can indicate the codepoint in octal code using the `\` 46 | // backslash before the octal value of the char 47 | page.push_content(page.draw_base_text('Test string. Euro symbol: \200', 10, 10, fnt_params)) 48 | 49 | // write €ABC using hex value of the chars, 3-byte UTF-8 BOM 50 | page.push_content(page.draw_raw_text('<80 41 42 43>', 10, 32, fnt_params)) 51 | 52 | // write the string as unicode bytes in the pdf 53 | e := 'ciao \200' 54 | page.push_content(page.draw_unicode_text(e, 10, 52, fnt_params)) 55 | 56 | // render the PDF 57 | txt := doc.render()! 58 | 59 | // write it to a file 60 | os.write_file_array('example09.pdf', txt)! 61 | } 62 | -------------------------------------------------------------------------------- /examples/example_01_raw_pdf.v: -------------------------------------------------------------------------------- 1 | import pdf 2 | import os 3 | 4 | fn main() { 5 | mut doc := pdf.Pdf{} 6 | doc.init() 7 | 8 | page_n := doc.create_page(pdf.Page_params{}) 9 | mut page := &doc.page_list[page_n] 10 | 11 | // add default PDF Courier font, no need to include it 12 | mut font_obj := pdf.Obj{ 13 | id: doc.get_new_id() 14 | } 15 | font_name := 'Helvetica' 16 | font_obj.fields << '/Name /F1 /Type /Font /Subtype /Type1 /BaseFont /Courier /Encoding /MacRomanEncoding' 17 | doc.obj_list << font_obj 18 | page.resources << '/Font << /F1 ${font_obj.id} 0 R >>' 19 | 20 | // add a jpeg to the pdf resources 21 | jpeg_data := os.read_bytes('data/v.jpg') or { panic(err) } 22 | jpeg_id := doc.add_jpeg_resource(jpeg_data) 23 | 24 | // use the jpeg in our page 25 | page.use_jpeg(jpeg_id) 26 | 27 | // create the page Content 28 | mut content := pdf.Obj{ 29 | id: doc.get_new_id() 30 | is_stream: true 31 | compress: true 32 | } 33 | 34 | // Our first string 35 | sent := 'Octal codepoints for font: ${font_name}' 36 | mut v_space := 24 37 | 38 | content.txt = ' 39 | % our jpeg :) 40 | q 41 | 128 0 0 128 0 200 cm 42 | /Image${jpeg_id} Do 43 | Q 44 | 45 | % our first string printed in ${font_name} 24 46 | q 47 | BT 48 | /F1 24 Tf 49 | 10 ${710 - v_space * 0} Td 50 | [(${sent})] TJ 51 | ET 52 | Q 53 | ' 54 | 55 | // Octal direct codepoint use, it depends on the font used 56 | content.txt += 'q' 57 | mut i := 0 58 | limit := 0o777 59 | for i < limit { 60 | mut txt1 := '' 61 | start_i := i 62 | step_len := i + 40 63 | for i < limit && i < step_len { 64 | txt1 += '\\${i:03o}' 65 | i++ 66 | } 67 | content.txt += ' 68 | BT 69 | /F1 10 Tf 70 | 10 ${710 - v_space} Td 71 | [(0o${start_i:03o} => ${txt1})] TJ 72 | ET 73 | ' 74 | v_space += 10 75 | } 76 | content.txt += 'Q' 77 | 78 | // add the page Object to the PDF 79 | doc.add_page_obj(mut page, content) 80 | 81 | // render the PDF 82 | txt := doc.render()! 83 | 84 | // write it to a file 85 | os.write_file_array('example01.pdf', txt)! 86 | } 87 | -------------------------------------------------------------------------------- /examples/example_05_simple_book.v: -------------------------------------------------------------------------------- 1 | import pdf 2 | import os 3 | 4 | const file_name = 'alice_in_wonderland' 5 | 6 | const page_iso_format = 'A4' 7 | const pg_fmt = pdf.page_fmt[page_iso_format] 8 | const text_box = pdf.Box{ 9 | x: pg_fmt.x + 30 10 | y: 20 11 | w: pg_fmt.w - 60 12 | h: pg_fmt.h - 40 13 | } 14 | 15 | fn main() { 16 | mut src_txt := os.read_file('data/${file_name}.txt') or { panic(err) } 17 | 18 | mut doc := pdf.Pdf{} 19 | doc.init() 20 | 21 | mut fnt_params := pdf.Text_params{ 22 | font_size: 12 23 | font_name: 'Helvetica' 24 | text_align: .left 25 | s_color: pdf.RGB{ 26 | r: 0 27 | g: 0 28 | b: 0 29 | } 30 | f_color: pdf.RGB{ 31 | r: 0 32 | g: 0 33 | b: 0 34 | } 35 | } 36 | 37 | // Declare the base (Type1 font) we want use 38 | if !doc.use_base_font(fnt_params.font_name) { 39 | eprintln('ERROR: Font ${fnt_params.font_name} not available!') 40 | return 41 | } 42 | 43 | mut tmp_res := false 44 | mut lo_txt := ' ' 45 | 46 | // render the pages 47 | for src_txt.len > 0 { 48 | page_n := doc.create_page(pdf.Page_params{ 49 | format: page_iso_format 50 | gen_content_obj: true 51 | compress: true 52 | }) 53 | mut page := &doc.page_list[page_n] 54 | page.user_unit = pdf.mm_unit 55 | //----- Page text ----- 56 | tmp_res, lo_txt, _ = page.text_box(src_txt, text_box, fnt_params) 57 | src_txt = lo_txt 58 | } 59 | // render the headers and footers 60 | mut index := 0 61 | for index < doc.page_list.len { 62 | mut page := &doc.page_list[index] 63 | //----- Header ----- 64 | fnt_params.font_size = 8.0 65 | header := '${file_name}' 66 | fnt_params.text_align = .center 67 | page.text_box(header, pdf.Box{ 68 | x: 10 69 | y: 12 70 | w: pg_fmt.w - 20 71 | h: 20 72 | }, fnt_params) 73 | 74 | //----- Footer ----- 75 | fnt_params.font_size = 8.0 76 | footer := 'Page ${index + 1} of ${doc.page_list.len}' 77 | fnt_params.text_align = .right 78 | page.text_box(footer, pdf.Box{ 79 | x: 10 80 | y: pg_fmt.h - 10 81 | w: pg_fmt.w - 30 82 | h: 20 83 | }, fnt_params) 84 | 85 | index++ 86 | } 87 | 88 | // render the PDF 89 | txt := doc.render()! 90 | 91 | // write it to a file 92 | os.write_file_array('example05.pdf', txt)! 93 | } 94 | -------------------------------------------------------------------------------- /examples/example_02_raw_pages.v: -------------------------------------------------------------------------------- 1 | import pdf 2 | import os 3 | import math 4 | 5 | fn main() { 6 | mut doc := pdf.Pdf{} 7 | doc.init() 8 | 9 | page_n := doc.create_page(pdf.Page_params{}) 10 | // println("page index $page_n") 11 | mut page := &doc.page_list[page_n] 12 | page.set_unit('mm') 13 | 14 | // add font 15 | mut font_obj := pdf.Obj{ 16 | id: doc.get_new_id() 17 | } 18 | font_obj.fields << '/Name /F1 /Type /Font /Subtype /Type1 /BaseFont /Courier /Encoding /MacRomanEncoding' 19 | doc.obj_list << font_obj 20 | page.resources << '/Font << /F1 ${font_obj.id} 0 R >>' 21 | 22 | jpeg_data := os.read_bytes('data/v.jpg') or { panic(err) } 23 | jpeg_id := doc.add_jpeg_resource(jpeg_data) 24 | page.use_jpeg(jpeg_id) 25 | 26 | // Content 27 | mut b := pdf.Obj{ 28 | id: doc.get_new_id() 29 | is_stream: true 30 | compress: true 31 | } 32 | 33 | sent := 'This is the first V working PDF!' 34 | v_space := 24 35 | 36 | b.txt = ' 37 | q 38 | 128 0 0 128 0 200 cm 39 | /Image${jpeg_id} Do 40 | Q 41 | 42 | q 43 | BT 44 | /F1 24 Tf 45 | 10 ${710 - v_space * 0} Td 46 | (${sent}) Tj 47 | ET 48 | 49 | BT 50 | 10 ${710 - v_space * 1} Td 51 | 0 Tr 52 | 0.5 g 53 | (${sent}) Tj 54 | ET 55 | 56 | BT 57 | 10 ${710 - v_space * 2} Td 58 | 1 Tr 59 | 0.3 w 60 | (${sent}) Tj 61 | ET 62 | 63 | BT 64 | 10 ${710 - v_space * 3} Td 65 | 1 Tr 66 | 0.2 w 67 | 0.5 g 68 | (${sent}) Tj 69 | ET 70 | Q 71 | 72 | BT 73 | /F1 20 Tf 74 | 10 ${710 - v_space * 4} Td 75 | (This ) Tj 76 | -10 Ts 77 | (text ) Tj 78 | 10 Ts 79 | (moves ) Tj 80 | 0 Ts 81 | (around) Tj 82 | ET 83 | 84 | ' 85 | // add circle of text 86 | n := 32 87 | for ang in 1 .. n { 88 | a := ((math.pi * 2.0) / f64(n + 1)) * f64(ang) 89 | c := (1.0 / f64(n + 1)) * f64(ang) 90 | cos_v := math.cos(a) 91 | sin_v := math.sin(a) 92 | b.txt += ' 93 | BT 94 | /F1 8 Tf 95 | 0 Tr 96 | ${c} 0 0 RG 97 | ${c} 0 0 rg 98 | ${cos_v} ${-sin_v} ${sin_v} ${cos_v} 306 300 Tm 99 | (${sent}) Tj 100 | ET 101 | ' 102 | } 103 | 104 | b.parts << 'BT 10 100 Td (Ciao da me Dario!)Tj ET' 105 | 106 | doc.add_page_obj(mut page, b) 107 | 108 | // page 2 109 | page_2 := doc.create_page(pdf.Page_params{}) 110 | page = &doc.page_list[page_2] 111 | // font used 112 | page.resources << '/Font << /F1 ${font_obj.id} 0 R >>' 113 | 114 | // add jepg to the page 115 | page.use_jpeg(jpeg_id) 116 | 117 | // Content 118 | mut b1 := pdf.Obj{ 119 | id: doc.get_new_id() 120 | is_stream: true 121 | compress: false 122 | } 123 | str1 := ' Second page!' 124 | b1.txt = 'BT /F1 20 Tf 10 700 Td (${str1})Tj ET\n' 125 | b1.parts << 'BT /F1 20 Tf 10 600 Td (${str1}2!)Tj ET\n' 126 | b1.parts << 'BT /F1 20 Tf 10 550 Td (${str1}3!)Tj ET\n' 127 | 128 | a := (math.pi / 180.0) * 30.0 129 | cos_v := math.cos(a) 130 | sin_v := math.sin(a) 131 | sz := 128.0 132 | b1.parts << 'q ${sz * cos_v} ${-sin_v * sz} ${sin_v * sz} ${cos_v * sz} 0 200 cm /Image${jpeg_id} Do Q ' 133 | // b1.parts << "q 128 0 0 128 0 200 cm /Image$jpeg_id Do Q" 134 | 135 | doc.add_page_obj(mut page, b1) 136 | 137 | txt := doc.render()! 138 | 139 | // write it to a file 140 | os.write_file_array('example02.pdf', txt)! 141 | } 142 | -------------------------------------------------------------------------------- /examples/example_03_two_columns.v: -------------------------------------------------------------------------------- 1 | import pdf 2 | import os 3 | 4 | fn main() { 5 | mut doc := pdf.Pdf{} 6 | doc.init() 7 | 8 | page_n := doc.create_page(pdf.Page_params{ 9 | format: 'A4' 10 | gen_content_obj: true 11 | compress: true 12 | }) 13 | mut page := &doc.page_list[page_n] 14 | page.user_unit = pdf.mm_unit 15 | 16 | mut fnt_params := pdf.Text_params{ 17 | font_size: 22.0 18 | font_name: 'Helvetica' 19 | render_mode: -1 20 | word_spacing: -1 21 | s_color: pdf.RGB{ 22 | r: 0 23 | g: 0 24 | b: 0 25 | } 26 | f_color: pdf.RGB{ 27 | r: 0 28 | g: 0 29 | b: 0 30 | } 31 | } 32 | 33 | // Declare the base (Type1 font) we want use 34 | if !doc.use_base_font(fnt_params.font_name) { 35 | eprintln('ERROR: Font ${fnt_params.font_name} not available!') 36 | return 37 | } 38 | 39 | //----- test box text ----- 40 | fnt_params.word_spacing = 0 41 | fnt_params.font_size = 12 42 | 43 | mut my_str := "Quicksort (sometimes called partition-exchange sort) is an efficient sorting algorithm. 44 | Developed by British computer scientist Tony Hoare in 1959 and published in 1961, it is still a commonly used algorithm for sorting. When implemented well, it can be about two or three times faster than its main competitors, merge sort and heapsort. 45 | Quicksort is a divide-and-conquer algorithm. It works by selecting a 'pivot' element from the array and partitioning the other elements into two sub-arrays, according to whether they are less than or greater than the pivot. The sub-arrays are then sorted recursively. This can be done in-place, requiring small additional amounts of memory to perform the sorting. 46 | Quicksort is a comparison sort, meaning that it can sort items of any type for which a 'less-than' relation (formally, a total order) is defined. Efficient implementations of Quicksort are not a stable sort, meaning that the relative order of equal sort items is not preserved. 47 | Mathematical analysis of quicksort shows that, on average, the algorithm takes O[n log n] comparisons to sort n items. In the worst case, it makes O[n2] comparisons, though this behavior is rare. 48 | Best solutions can be available! 49 | Soon or later they will be available." 50 | 51 | my_str = my_str + my_str 52 | my_str = my_str + my_str 53 | 54 | //----- Text Area ----- 55 | tb := pdf.Box{ 56 | x: page.media_box.x / page.user_unit + 10 57 | y: 20 58 | w: page.media_box.w / page.user_unit - 20 59 | h: page.media_box.h / page.user_unit - 20 60 | } 61 | 62 | // justify align 63 | fnt_params.text_align = .justify 64 | mut tmp_txt := my_str 65 | mut tmp_res := false 66 | mut lo_txt := ' ' 67 | 68 | // set two columns 69 | boxes := [ 70 | pdf.Box{ 71 | x: tb.x 72 | y: tb.y 73 | w: tb.w / 2 - 10 74 | h: tb.h - 20 75 | }, 76 | pdf.Box{ 77 | x: tb.x + tb.w / 2 + 5 78 | y: tb.y 79 | w: tb.w / 2 - 10 80 | h: tb.h - 20 81 | }, 82 | ] 83 | 84 | for bx in boxes { 85 | if lo_txt.len > 0 { 86 | tmp_res, lo_txt, _ = page.text_box(tmp_txt, bx, fnt_params) 87 | if tmp_res { 88 | break 89 | } 90 | tmp_txt = lo_txt 91 | // println("leftover: [${lo_txt}]") 92 | } 93 | } 94 | // println("res: ${tmp_res} left_over: [${lo_txt}]") 95 | 96 | // render the PDF 97 | txt := doc.render()! 98 | 99 | // write it to a file 100 | os.write_file_array('example03.pdf', txt)! 101 | } 102 | -------------------------------------------------------------------------------- /examples/example_08_ttf_font.v: -------------------------------------------------------------------------------- 1 | import pdf 2 | import os 3 | 4 | fn main() { 5 | mut doc := pdf.Pdf{} 6 | doc.init() 7 | 8 | //************* 9 | // Page 1 10 | //************* 11 | page_n := doc.create_page(pdf.Page_params{}) 12 | mut page := &doc.page_list[page_n] 13 | 14 | mut content := pdf.Obj{ 15 | id: doc.get_new_id() 16 | is_stream: true 17 | compress: false 18 | } 19 | 20 | // add TTF font obj 21 | doc.load_ttf_font('data/Graduate-Regular.ttf', 'GR', 1.0) 22 | doc.load_ttf_font('data/ShelleyAllegro.ttf', 'SA', 2.0) 23 | 24 | // Raw PDF usage 25 | content.txt = ' 26 | % our first string printed with TTF fonts 27 | q' 28 | 29 | mut count := 0 30 | for _, v in doc.ttf_font_used { 31 | content.txt += ' 32 | BT 33 | /${v.font_name} 20 Tf 34 | 10 ${780 - 20 * count} Td 35 | (Prova Font ${v.font_name} #!=> Ancora di nuovo!!) Tj 36 | ET 37 | ' 38 | count++ 39 | 40 | content.txt += ' 41 | BT 42 | /${v.font_name} 20 Tf 43 | 10 ${780 - 20 * count} Td 44 | (AAAAA ${v.font_name} #!=> Again PDF for V tests!!) Tj 45 | ET 46 | ' 47 | 48 | count++ 49 | } 50 | 51 | content.compress = true 52 | // add the page Object to the PDF 53 | doc.add_page_obj(mut page, content) 54 | 55 | //************* 56 | // Page 2 57 | //************* 58 | // simplified font usage 59 | 60 | page_n1 := doc.create_page(pdf.Page_params{ 61 | format: 'A4' 62 | gen_content_obj: true 63 | compress: false 64 | }) 65 | page = &doc.page_list[page_n1] 66 | page.user_unit = pdf.mm_unit 67 | 68 | mut fnt_params := pdf.Text_params{ 69 | font_size: 14.0 70 | font_name: 'SA' 71 | s_color: pdf.RGB{ 72 | r: 0 73 | g: 0 74 | b: 0 75 | } 76 | f_color: pdf.RGB{ 77 | r: 0 78 | g: 0 79 | b: 0 80 | } 81 | } 82 | 83 | mut my_str := "Quicksort (sometimes called partition-exchange sort) is an efficient sorting algorithm. 84 | Developed by British computer scientist Tony Hoare in 1959 and published in 1961, it is still a commonly used algorithm for sorting. When implemented well, it can be about two or three times faster than its main competitors, merge sort and heapsort. 85 | Quicksort is a divide-and-conquer algorithm. It works by selecting a 'pivot' element from the array and partitioning the other elements into two sub-arrays, according to whether they are less than or greater than the pivot. The sub-arrays are then sorted recursively. This can be done in-place, requiring small additional amounts of memory to perform the sorting. 86 | Quicksort is a comparison sort, meaning that it can sort items of any type for which a 'less-than' relation (formally, a total order) is defined. Efficient implementations of Quicksort are not a stable sort, meaning that the relative order of equal sort items is not preserved. 87 | Mathematical analysis of quicksort shows that, on average, the algorithm takes O[n log n] comparisons to sort n items. In the worst case, it makes O[n2] comparisons, though this behavior is rare. 88 | Best solutions can be available! 89 | Soon or later they will be available." 90 | 91 | my_str = my_str + my_str 92 | my_str = my_str + my_str 93 | 94 | //----- Text Area ----- 95 | tb := pdf.Box{ 96 | x: page.media_box.x / page.user_unit + 10 97 | y: 20 98 | w: page.media_box.w / page.user_unit - 20 99 | h: page.media_box.h / page.user_unit - 20 100 | } 101 | 102 | // justify align 103 | fnt_params.text_align = .justify 104 | mut tmp_txt := my_str 105 | mut tmp_res := false 106 | mut lo_txt := ' ' 107 | 108 | // set two columns 109 | boxes := [ 110 | pdf.Box{ 111 | x: tb.x 112 | y: tb.y 113 | w: tb.w / 2 - 10 114 | h: tb.h - 20 115 | }, 116 | pdf.Box{ 117 | x: tb.x + tb.w / 2 + 5 118 | y: tb.y 119 | w: tb.w / 2 - 10 120 | h: tb.h - 20 121 | }, 122 | ] 123 | 124 | for bx in boxes { 125 | if lo_txt.len > 0 { 126 | tmp_res, lo_txt, _ = page.text_box(tmp_txt, bx, fnt_params) 127 | if tmp_res { 128 | break 129 | } 130 | tmp_txt = lo_txt 131 | // println("leftover: [${lo_txt}]") 132 | } 133 | } 134 | 135 | /******************************************** 136 | * 137 | * Rendering 138 | * 139 | * ******************************************/ 140 | 141 | // render the PDF 142 | txt := doc.render()! 143 | 144 | // write it to a file 145 | os.write_file_array('example08.pdf', txt)! 146 | } 147 | -------------------------------------------------------------------------------- /examples/example_04_lin_gradient.v: -------------------------------------------------------------------------------- 1 | import pdf 2 | import math 3 | import os 4 | 5 | fn main() { 6 | mut doc := pdf.Pdf{} 7 | doc.init() 8 | 9 | page_n := doc.create_page(pdf.Page_params{ 10 | format: 'A4' 11 | gen_content_obj: true 12 | compress: true 13 | }) 14 | mut page := &doc.page_list[page_n] 15 | page.user_unit = pdf.mm_unit 16 | 17 | mut fnt_params := pdf.Text_params{ 18 | font_size: 22.0 19 | font_name: 'Helvetica' 20 | render_mode: -1 21 | word_spacing: -1 22 | s_color: pdf.RGB{ 23 | r: 0 24 | g: 0 25 | b: 0 26 | } 27 | f_color: pdf.RGB{ 28 | r: 0 29 | g: 0 30 | b: 0 31 | } 32 | } 33 | 34 | // Declare the base (Type1 font) we want use 35 | if !doc.use_base_font(fnt_params.font_name) { 36 | eprintln('ERROR: Font ${fnt_params.font_name} not available!') 37 | return 38 | } 39 | 40 | //----- Create gradients ----- 41 | // rgb(83,107,138) V blue 1 42 | // rgb(93,135,191) V blue 2 43 | angle := 270.0 44 | doc.create_linear_gradient_shader('vertical_gradient', pdf.RGB{ 45 | r: 83 / 255.0 46 | g: 107 / 255.0 47 | b: 138 / 255.0 48 | }, pdf.RGB{ 49 | r: 93 / 255.0 50 | g: 135 / 255.0 51 | b: 191 / 255.0 52 | }, f32(angle * math.pi / 180.0)) 53 | page.use_shader('vertical_gradient') 54 | 55 | // draw vertical gradient 56 | page.push_content(page.draw_gradient_box('vertical_gradient', pdf.Box{ 57 | x: 10 58 | y: 10 59 | w: 10 60 | h: page.media_box.h / page.user_unit - 30 61 | }, page.media_box.h / page.user_unit - 20 * 2)) 62 | 63 | //----- Create gradient for the separator ----- 64 | doc.create_linear_gradient_shader('sep_grad', pdf.RGB{ 65 | r: 83 / 255.0 66 | g: 107 / 255.0 67 | b: 138 / 255.0 68 | }, pdf.RGB{ 69 | r: 93 / 255.0 70 | g: 135 / 255.0 71 | b: 191 / 255.0 72 | }, 0) 73 | page.use_shader('sep_grad') 74 | 75 | //----- Text Area ----- 76 | tb := pdf.Box{ 77 | x: page.media_box.x / page.user_unit + 30 78 | y: 20 79 | w: page.media_box.w / page.user_unit - 40 80 | h: page.media_box.h / page.user_unit - 20 81 | } 82 | 83 | //----- Header ----- 84 | fnt_params.font_size = 6.0 85 | header := 'V manual PDF\nBest Header ever!' 86 | fnt_params.text_align = .left 87 | mut res1, _, mut last_y := page.text_box(header, pdf.Box{ 88 | x: 10 89 | y: 0 90 | w: tb.w 91 | h: 20 92 | }, fnt_params) 93 | 94 | //----- Footer ----- 95 | fnt_params.font_size = 8.0 96 | footer := 'Page 1 of 1\nBest footer ever!' 97 | fnt_params.text_align = .right 98 | res1, _, last_y = page.text_box(footer, pdf.Box{ 99 | x: tb.x 100 | y: page.media_box.h / page.user_unit - 10 101 | w: tb.w 102 | h: 20 103 | }, fnt_params) 104 | 105 | //----- Title ----- 106 | fnt_params.font_size = 36.0 107 | mut y_pos := tb.y 108 | title := 'V test page' 109 | // mut width, mut ascender, mut descender := page.calc_string_bb(title, fnt_params) 110 | mut width, _, _ := page.calc_string_bb(title, fnt_params) 111 | page.push_content(page.draw_base_text(title, tb.x + (tb.w / 2) - width / 2, y_pos, 112 | fnt_params)) 113 | y_pos += page.new_line_offset(fnt_params) 114 | 115 | //----- Sub Title ----- 116 | fnt_params.font_size = 22.0 117 | fnt_params.leading = 0.2 118 | sub_title := 'A first V page with gradients' 119 | width, _, _ = page.calc_string_bb(sub_title, fnt_params) 120 | page.push_content(page.draw_base_text(sub_title, tb.x + (tb.w / 2) - width / 2, y_pos, 121 | fnt_params)) 122 | y_pos += page.new_line_offset(fnt_params) 123 | 124 | //----- test Text ------ 125 | mut p_txt := "Quicksort (sometimes called partition-exchange sort) is an efficient sorting algorithm. 126 | Developed by British computer scientist Tony Hoare in 1959 and published in 1961, it is still a commonly used algorithm for sorting. When implemented well, it can be about two or three times faster than its main competitors, merge sort and heapsort. 127 | Quicksort is a divide-and-conquer algorithm. It works by selecting a 'pivot' element from the array and partitioning the other elements into two sub-arrays, according to whether they are less than or greater than the pivot. The sub-arrays are then sorted recursively. This can be done in-place, requiring small additional amounts of memory to perform the sorting. 128 | Quicksort is a comparison sort, meaning that it can sort items of any type for which a 'less-than' relation (formally, a total order) is defined. Efficient implementations of Quicksort are not a stable sort, meaning that the relative order of equal sort items is not preserved." 129 | 130 | fnt_params.font_size = 12.0 131 | 132 | //----- justify align ----- 133 | fnt_params.text_align = .justify 134 | res1, _, last_y = page.text_box(p_txt, pdf.Box{ 135 | x: tb.x 136 | y: y_pos 137 | w: tb.w 138 | h: 70 139 | }, fnt_params) 140 | y_pos = last_y 141 | 142 | //----- Separator ------ 143 | page.push_content(page.draw_gradient_box('sep_grad', pdf.Box{ 144 | x: tb.x 145 | y: y_pos 146 | w: tb.w 147 | h: 0.5 148 | }, page.media_box.h / page.user_unit - 20 * 2)) 149 | y_pos += 6 150 | 151 | //----- right align ----- 152 | fnt_params.text_align = .right 153 | res1, _, last_y = page.text_box(p_txt, pdf.Box{ 154 | x: tb.x 155 | y: y_pos 156 | w: tb.w 157 | h: 70 158 | }, fnt_params) 159 | y_pos = last_y + 10 160 | 161 | //----- left align ----- 162 | fnt_params.text_align = .left 163 | res1, _, last_y = page.text_box(p_txt, pdf.Box{ 164 | x: tb.x 165 | y: y_pos 166 | w: tb.w 167 | h: 70 168 | }, fnt_params) 169 | 170 | // render the PDF 171 | txt := doc.render()! 172 | 173 | // write it to a file 174 | os.write_file_array('example04.pdf', txt)! 175 | } 176 | -------------------------------------------------------------------------------- /ttf_font.v: -------------------------------------------------------------------------------- 1 | module pdf 2 | 3 | import x.ttf 4 | import os 5 | import strings 6 | import compress.zlib 7 | 8 | /****************************************************************************** 9 | * 10 | * TTF font management 11 | * 12 | ******************************************************************************/ 13 | @[heap] 14 | struct TtfFontRsc { 15 | pub mut: 16 | id_font_file int 17 | id_font int 18 | id_font_desc int 19 | 20 | tf ttf.TTF_File 21 | font_name_id int 22 | font_name string 23 | full_name string 24 | 25 | pdf_font_id int 26 | pdf_font_descriptor_id int 27 | pdf_ttffont_file_id int 28 | 29 | flags u16 30 | first_char int = 1 31 | last_char int 32 | widths []int 33 | fontbbox []int 34 | ascent int 35 | descent int 36 | } 37 | 38 | /* 39 | fn get_ttf_widths(mut tf ttf.TTF_File) []int { 40 | count := tf.glyph_count() 41 | mut widths := []int 42 | for i in 0..count { 43 | x_min, x_max, _, _ := tf.read_glyph_dim(i) 44 | widths << (x_max - x_min) * 0 45 | } 46 | return widths 47 | } 48 | */ 49 | 50 | fn render_ttf_files(mut res_c strings.Builder, tf TtfFontRsc) !int { 51 | buf := zlib.compress(tf.tf.buf) or { return error('compress failed') } 52 | res_c.write('${tf.id_font_file} 0 obj\n'.bytes())! 53 | 54 | // mandatory fields in a compress obj stream 55 | res_c.write('<>\n'.bytes())! 56 | res_c.write('stream\n'.bytes())! 57 | res_c.write(buf)! 58 | res_c.write('\nendstream\n'.bytes())! 59 | res_c.write('endobj\n\n'.bytes())! 60 | return int(res_c.len) 61 | } 62 | 63 | fn render_ttf_font(mut res_c strings.Builder, tf TtfFontRsc) !int { 64 | widths := pdf_format_width(tf.widths)! 65 | full_name := tf.full_name.replace(' ', '_') 66 | res_c.write('${tf.id_font} 0 obj\n'.bytes())! 67 | res_c.write('<< 68 | /Type/Font 69 | /Name/${tf.font_name} 70 | /Subtype/TrueType 71 | /BaseFont/${full_name} 72 | /Encoding/WinAnsiEncoding 73 | /FirstChar ${tf.first_char} 74 | /LastChar ${tf.last_char} 75 | /FontDescriptor ${tf.id_font_desc} 0 R 76 | /Widths${widths} 77 | >> 78 | endobj\n 79 | '.bytes())! 80 | return int(res_c.len) 81 | } 82 | 83 | // /Widths${widths} 84 | 85 | fn render_ttf_font_decriptor(mut res_c strings.Builder, tf TtfFontRsc) !int { 86 | mut panose := '' 87 | for p_val in tf.tf.panose_array { 88 | panose += '${p_val} ' 89 | } 90 | full_name := tf.full_name.replace(' ', '_') 91 | fontbbox := pdf_format_width(tf.fontbbox)! 92 | res_c.write('${tf.id_font_desc} 0 obj\n'.bytes())! 93 | res_c.write('<< 94 | /Type/FontDescriptor 95 | /FontName/${full_name} 96 | /FontBBox${fontbbox} 97 | /Flags ${tf.flags} 98 | /Ascent ${tf.ascent} 99 | /Descent ${tf.descent} 100 | /StemV 80 101 | /ItalicAngle 0 102 | /FontFile2 ${tf.id_font_file} 0 R 103 | /Style << /Panose < ${panose} > >> 104 | >> 105 | endobj\n 106 | '.bytes())! 107 | return int(res_c.len) 108 | } 109 | 110 | fn pdf_format_width(w []int) !string { 111 | mut bs := strings.new_builder(4096) 112 | bs.write('['.bytes())! 113 | for x in w { 114 | bs.write(' ${x}'.bytes())! 115 | } 116 | bs.write(' ]'.bytes())! 117 | return bs.str() 118 | } 119 | 120 | pub fn (mut p Pdf) load_ttf_font(font_path string, font_name string, width_scale f32) { 121 | mut tf_rsc := TtfFontRsc{} 122 | 123 | tf_rsc.id_font_file = p.id_count + 1 124 | tf_rsc.id_font = p.id_count + 2 125 | tf_rsc.id_font_desc = p.id_count + 3 126 | p.id_count += 3 127 | 128 | mut tf := ttf.TTF_File{} 129 | tf.width_scale = width_scale 130 | tf.buf = os.read_bytes(font_path) or { panic(err) } 131 | // println('TrueTypeFont file [$font_path] len: $tf.buf.len') 132 | tf.init() 133 | tf_rsc.tf = tf 134 | 135 | tf_rsc.flags = tf.flags 136 | // println("desc:\n${tf}") 137 | tf_rsc.fontbbox << int(tf.x_min) 138 | tf_rsc.fontbbox << int(tf.y_min) 139 | tf_rsc.fontbbox << int(tf.x_max) 140 | tf_rsc.fontbbox << int(tf.y_max) 141 | // println("FontBBox: ${tf_rsc.fontbbox}") 142 | tf_rsc.ascent = tf.ascent 143 | tf_rsc.descent = tf.descent 144 | 145 | tf_rsc.widths, tf_rsc.first_char, tf_rsc.last_char = tf.get_ttf_widths() 146 | // tf_rsc.first_char = 1 147 | // tf_rsc.last_char = tf_rsc.widths.len 148 | 149 | // println("Widths ${tf_rsc.widths.len} :${tf_rsc.widths}") 150 | tf_rsc.font_name = font_name 151 | tf_rsc.full_name = tf.full_name 152 | 153 | p.ttf_font_used[font_name] = tf_rsc 154 | 155 | // tf_rsc.render_font() or {println("Error")} 156 | } 157 | 158 | /* 159 | pub fn (mut p TtfFontRsc) render_object() string { 160 | return " 161 | ${pdf_font_id} 0 obj 162 | << 163 | /LastChar 255 164 | /BaseFont/GravitasOne 165 | /Type/Font 166 | /Subtype/TrueType 167 | /Encoding/WinAnsiEncoding 168 | /FirstChar 1 169 | /Widths[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 369 445 468 722 863 908 911 268 478 478 509 522 399 524 396 461 878 572 802 797 751 809 824 750 817 824 396 399 398 526 398 752 1198 923 940 901 988 904 818 937 1093 604 641 1026 807 1078 911 952 901 952 991 863 849 924 878 1270 934 864 810 478 461 478 666 746 459 899 906 775 906 774 574 805 945 525 558 892 525 1356 945 824 920 906 773 761 604 932 765 1139 827 767 806 461 257 461 726 0 908 0 333 0 607 0 0 0 475 0 0 474 1290 0 0 0 0 333 333 602 602 475 637 1137 513 0 0 474 1189 0 0 0 369 445 775 895 674 864 257 724 459 873 582 743 617 524 873 459 466 523 542 515 459 932 678 396 459 408 558 743 955 1054 1015 752 923 923 923 923 923 923 1187 901 904 904 904 904 604 604 604 604 988 911 952 952 952 952 952 523 952 924 924 924 924 864 948 1013 899 899 899 899 899 899 1199 775 774 774 774 774 525 525 525 525 791 945 824 824 824 824 824 523 824 932 932 932 932 765 948 765] 170 | /FontDescriptor ${pdf_font_descriptor_id} 0 R 171 | >> 172 | endobj 173 | ${pdf_font_descriptor_id} 0 obj 174 | << 175 | /CapHeight 691 176 | /FontBBox[-37 -326 1340 945] 177 | /Type/FontDescriptor 178 | /FontFile2 1 0 R 179 | /Descent -326 180 | /StemV 80 181 | /Flags 32 182 | /Ascent 945 183 | /FontName/GravitasOne 184 | /ItalicAngle 0 185 | >> 186 | " 187 | } 188 | */ 189 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vPDF 2 | 3 | ## Introduction 4 | 5 | From wikipedia: 6 | 7 | *The Portable Document Format (PDF) is a file format developed by Adobe in the 1990s to present documents, including text formatting and images, in a manner independent of application software, hardware, and operating systems. Based on the PostScript language, each PDF file encapsulates a complete description of a fixed-layout flat document, including the text, fonts, vector graphics, raster images and other information needed to display it. PDF was standardized as ISO 32000 in 2008, and no longer requires any royalties for its implementation.* 8 | 9 | PDF is a commonly used file format, but producing a PDF file is not a simple task. 10 | 11 | This module was created to simplify PDF file creation using the [V programming language](https://vlang.io/). 12 | 13 | **vPDF** is structured in two layers, low and high. 14 | 15 | ### Low level layer 16 | 17 | At the lower level, it is possible to create a PDF and write directly to it. This layer is intended for users that are familiar with the PDF format, and wish to create files with as little overhead as possible. You will, however, have to keep track of all the details yourself. 18 | 19 | ### High level layer 20 | 21 | At the higher level, there are various functions which simplify creating a PDF, and no knowledge of the PDF format is required to use them. 22 | 23 | ## Installation 24 | 25 | ``` 26 | v install pdf 27 | ``` 28 | 29 | ## QuickStart 30 | 31 | Let's start with a reasonably simple example: creating a PDF with only one page and with a simple string on it. For this example we will use the high level layer of **vPDF**: 32 | 33 | ### Complete source (example 06) 34 | 35 | ```v 36 | import pdf 37 | import os 38 | 39 | fn main() { 40 | mut doc := pdf.Pdf{} 41 | doc.init() 42 | 43 | page_n := doc.create_page(pdf.Page_params{ 44 | format: 'A4' 45 | gen_content_obj: true 46 | compress: false 47 | }) 48 | mut page := &doc.page_list[page_n] 49 | page.user_unit = pdf.mm_unit 50 | 51 | mut fnt_params := pdf.Text_params{ 52 | font_size: 22.0 53 | font_name: 'Helvetica' 54 | s_color: pdf.RGB{ 55 | r: 0 56 | g: 0 57 | b: 0 58 | } 59 | f_color: pdf.RGB{ 60 | r: 0 61 | g: 0 62 | b: 0 63 | } 64 | } 65 | 66 | // Declare the base (Type1 font) we want use 67 | if !doc.use_base_font(fnt_params.font_name) { 68 | eprintln('ERROR: Font $fnt_params.font_name not available!') 69 | return 70 | } 71 | 72 | // write the string 73 | page.push_content(page.draw_base_text('My first string.', 10, 10, fnt_params)) 74 | 75 | // render the PDF 76 | txt := doc.render()! 77 | 78 | // write it to a file 79 | os.write_file_array('example06.pdf', txt)! 80 | } 81 | ``` 82 | 83 | Now we'll break down the code: 84 | 85 | ### PDF creation 86 | 87 | First we need to create a PDF and initialize the necessary structures: 88 | 89 | ```v 90 | mut doc := pdf.Pdf{} 91 | doc.init() 92 | ``` 93 | 94 | ### Page format and creation 95 | 96 | Once we have the PDF structure, we need to create the page or pages. The page can have several parameters such as: dimensions, a flag that indicates if we want **vPDF** to automatically create the objects or we want do it manually, etc. 97 | 98 | For this example we use the simplest configuration possible. We want to create an `A4` page (210x297 mm), we want **vPDF** to handle object creation and tracking for us, and we do not want to compress the output: 99 | 100 | ```v 101 | page_n := doc.create_page(pdf.Page_params{ 102 | format: 'A4' 103 | gen_content_obj: true 104 | compress: false 105 | }) 106 | ``` 107 | 108 | When the page is created, the `create_page` routine returns the index of the page in the **vPDF** page array. 109 | 110 | To modify the page, we need to set millimeters as the working unit size for the page: 111 | 112 | ```v 113 | mut page := &doc.page_list[page_n] // get the page struct 114 | page.user_unit = pdf.mm_unit // set millimeters for all operations 115 | ``` 116 | 117 | ### Font selection and use 118 | 119 | To write a simple string we need to tell the page which font we want to use, and its properties. 120 | 121 | First we create a `Text_params` *struct* that contains all the information **vPDF** needs to instantiate and use a font: 122 | 123 | We want a 22pt Helvetica font, and we set the font (stroke) color and fill color: 124 | 125 | ```v 126 | mut fnt_params := pdf.Text_params{ 127 | font_size : 22.0 128 | font_name : "Helvetica" 129 | s_color : {r:0,g:0,b:0} 130 | f_color : {r:0,g:0,b:0} 131 | } 132 | ``` 133 | 134 | At present, **vPDF** only supports Type1 fonts. The fonts available in this module are: `['Courier-Bold', 'Courier-BoldOblique', 'Courier-Oblique', 'Courier', 'Helvetica-Bold', 'Helvetica-BoldOblique', 'Helvetica-Oblique', 'Helvetica', 'Symbol', 'Times-Bold', 'Times-BoldItalic', 'Times-Italic', 'Times-Roman', 'ZapfDingbats']` 135 | 136 | Then tell **vPDF** the font we want use in the PDF file: 137 | 138 | ```v 139 | doc.use_base_font(fnt_params.font_name) 140 | ``` 141 | 142 | ### Write a string 143 | 144 | A PDF page is written in FIFO order, like a queue. Using the high level layer we don't need to care about creation of the objects or their indexing. 145 | 146 | To write a string at 10 mm from the left border and 10 mm from the top border of the page we only need to do: 147 | 148 | ```v 149 | page.push_content( 150 | page.draw_base_text("My first string.", 10, 10, fnt_params) 151 | ) 152 | ``` 153 | 154 | ### Render the PDF 155 | 156 | At this point our example PDF is complete, so tell **vPDF** to render the result. The rendering function returns a `strings.Builder`: 157 | 158 | ```v 159 | txt := doc.render() 160 | ``` 161 | 162 | ### Save the file 163 | 164 | With the returned `strings.builder`, we can do whatever we want - save the PDF to disk, return it as a HTTP response, or whatever else is needed. 165 | 166 | In this quickstart we will write the PDF file to disk: 167 | 168 | ```v 169 | os.write_file_array('example06.pdf', txt.buf) 170 | ``` 171 | 172 | ## Resulting PDF 173 | 174 | Since we chose not to compress the PDF, it can be opened in a simple text editor so we can read its raw form Note that you would normally open the file with a PDF reader - this is for illustrative purposes only. The contents of the PDF file as written by this example should be: 175 | 176 | ``` 177 | %PDF-1.4 178 | 1 0 obj 179 | << /Type /Catalog /Pages 2 0 R >> 180 | endobj 181 | 182 | 2 0 obj 183 | << /Type /Pages /Kids[ 3 0 R ] /Count 1 >> 184 | endobj 185 | 186 | 3 0 obj 187 | << /Type /Page /Parent 2 0 R /MediaBox [ 0 0 595.274 841.888 ] /CropBox [ 0 0 595.274 841.888 ] /Resources << /ProcSet [/PDF/ImageB/ImageC/ImageI/Text] /Font << /F0 5 0 R >> >> /Contents 4 0 R >> 188 | endobj 189 | 190 | 4 0 obj 191 | << /Length 77 >> 192 | stream 193 | 194 | BT 195 | /F0 22 Tf 196 | 0 0 0 RG 0 0 0 rg 197 | 28.3464 813.542 Td 198 | (My first string.) Tj 199 | ET 200 | 201 | endstream 202 | endobj 203 | 204 | 5 0 obj 205 | << /Name /F0 /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding /MacRomanEncoding >> 206 | endobj 207 | 208 | xref 209 | 0 1 210 | 0000000000 65535 f 211 | 1 1 212 | 0000000009 00000 n 213 | 2 1 214 | 0000000061 00000 n 215 | 3 1 216 | 0000000124 00000 n 217 | 4 1 218 | 0000000351 00000 n 219 | 5 1 220 | 0000000479 00000 n 221 | trailer 222 | <> 223 | startxref 224 | 589 225 | %%EOF 226 | ``` 227 | 228 | ## Add a JPEG image 229 | 230 | Now we can add an image to the page, it can be simply done adding the following lines just before the rendering operation: 231 | 232 | ```v 233 | // read a jpeg image from the disk 234 | jpeg_data := os.read_bytes("data/v.jpg") or { panic(err) } 235 | jpeg_id := doc.add_jpeg_resource(jpeg_data) 236 | // tell the page we want use a this jpeg in the page 237 | page.use_jpeg(jpeg_id) 238 | 239 | // get width and height in pixel of the jpeg image 240 | _, w, h := pdf.get_jpeg_info(jpeg_data) 241 | h_scale := h / w 242 | 243 | page.push_content( 244 | page.draw_jpeg(jpeg_id, {x:10, y:60, w:30, h:30 * h_scale}) 245 | ) 246 | ``` 247 | 248 | First we need to load in memory the image, this task is achieved with: `os.read_bytes("data/v.jpg")` 249 | 250 | Now we must add the jpeg to the resources of the PDF: `doc.add_jpeg_resource(jpeg_data)` that return an id that we will use to identify the jpeg as PDF resource. 251 | 252 | **Note:** *All the resources of a PDF file like images, fonts etc must be loaded and stored inside the PDF itself as PDF objects.* 253 | 254 | The **vPDF** module take care about the creation of the objects and their indexing. 255 | 256 | Now we must use the jpeg, this usage belong to the page and we must tell to the page that we want use a specific image, we do this with: `page.use_jpeg(jpeg_id)` 257 | 258 | Before draw the image we can collect some info on it using: `_, w, h := pdf.get_jpeg_info(jpeg_data)` in this case we need only the width and the height of the jpeg. 259 | 260 | Now we can draw our jpeg in the pdf using: 261 | 262 | ```v 263 | page.push_content( 264 | page.draw_jpeg(jpeg_id, {x:10, y:60, w:30, h:30 * h_scale}) 265 | ) 266 | ``` 267 | 268 | where we specify the `jpeg_id` returned by the `add_jpeg_resource` call and a `Box` with the position and dimension where we want the jpeg. 269 | 270 | In this case we will draw the jpeg at 10 mm from the left border and 60 mm from the top border with a width of 30mm and a height proportional to the source. 271 | 272 | ### Complete source (example 07) 273 | 274 | ```v 275 | import pdf 276 | import os 277 | 278 | fn main() { 279 | mut doc := pdf.Pdf{} 280 | doc.init() 281 | 282 | page_n := doc.create_page(pdf.Page_params{ 283 | format: 'A4' 284 | gen_content_obj: true 285 | compress: false 286 | }) 287 | mut page := &doc.page_list[page_n] 288 | page.user_unit = pdf.mm_unit 289 | 290 | mut fnt_params := pdf.Text_params{ 291 | font_size: 22.0 292 | font_name: 'Helvetica' 293 | s_color: pdf.RGB{ 294 | r: 0 295 | g: 0 296 | b: 0 297 | } 298 | f_color: pdf.RGB{ 299 | r: 0 300 | g: 0 301 | b: 0 302 | } 303 | } 304 | 305 | // Declare the base (Type1 font) we want use 306 | if !doc.use_base_font(fnt_params.font_name) { 307 | eprintln('ERROR: Font $fnt_params.font_name not available!') 308 | return 309 | } 310 | 311 | // write the string 312 | page.push_content(page.draw_base_text('My first string.', 10, 10, fnt_params)) 313 | 314 | // read a jpeg image from the disk 315 | jpeg_data := os.read_bytes('data/v.jpg') or { panic(err) } 316 | jpeg_id := doc.add_jpeg_resource(jpeg_data) 317 | // tell the page we want use a this jpeg in the page 318 | page.use_jpeg(jpeg_id) 319 | 320 | // get width and height in pixel of the jpeg image 321 | _, w, h := pdf.get_jpeg_info(jpeg_data) 322 | h_scale := h / w 323 | 324 | page.push_content(page.draw_jpeg(jpeg_id, pdf.Box{ 325 | x: 10 326 | y: 60 327 | w: 30 328 | h: 30 * h_scale 329 | })) 330 | 331 | // render the PDF 332 | txt := doc.render()! 333 | 334 | // write it to a file 335 | os.write_file_array('example07.pdf', txt)! 336 | } 337 | ``` 338 | 339 | ## Text Box 340 | 341 | A text box is a page's box where the text is fitted. 342 | 343 | It is an utility function that help write indented text. 344 | 345 | As input you need : 346 | 347 | - the source text 348 | - a Box with the coordinates and dimensions of the container box 349 | - a `Text_params` structure, the possible `text_align` values are: `left, center, right, justify` 350 | 351 | ```v 352 | source_txt := "text to write" 353 | text_bx := pdf.Box{x:10, y:10, w:40, h:60} 354 | nt_params := pdf.Text_params{ 355 | font_size : 22.0 356 | font_name : "Helvetica" 357 | text_align : .left 358 | } 359 | res, left_over_txt, bottom_y = page.text_box(source_txt, text_bx, fnt_params) 360 | ``` 361 | 362 | As output you will obtain: 363 | 364 | - `res` is true if all the `source_txt` lay inside the `text_bx` otherwise false. 365 | - `left_over_txt` is the text that was not possible to fit inside `text_bx` 366 | - `bottom_y` is the bottom y coordinate where the text was written by `text_box` function. 367 | 368 | You can use the `left_over_txt` as input for other `text_box` function like in the example 03 or example 05. 369 | 370 | ### Complete source (example 03) 371 | 372 | ```v 373 | import pdf 374 | import os 375 | 376 | fn main() { 377 | mut doc := pdf.Pdf{} 378 | doc.init() 379 | 380 | page_n := doc.create_page(pdf.Page_params{ 381 | format: 'A4' 382 | gen_content_obj: true 383 | compress: true 384 | }) 385 | mut page := &doc.page_list[page_n] 386 | page.user_unit = pdf.mm_unit 387 | 388 | mut fnt_params := pdf.Text_params{ 389 | font_size: 22.0 390 | font_name: 'Helvetica' 391 | render_mode: -1 392 | word_spacing: -1 393 | s_color: pdf.RGB{ 394 | r: 0 395 | g: 0 396 | b: 0 397 | } 398 | f_color: pdf.RGB{ 399 | r: 0 400 | g: 0 401 | b: 0 402 | } 403 | } 404 | 405 | // Declare the base (Type1 font) we want use 406 | if !doc.use_base_font(fnt_params.font_name) { 407 | eprintln('ERROR: Font $fnt_params.font_name not available!') 408 | return 409 | } 410 | 411 | //----- test box text ----- 412 | fnt_params.word_spacing = 0 413 | fnt_params.font_size = 12 414 | 415 | mut my_str := "Quicksort (sometimes called partition-exchange sort) is an efficient sorting algorithm. 416 | Developed by British computer scientist Tony Hoare in 1959 and published in 1961, it is still a commonly used algorithm for sorting. When implemented well, it can be about two or three times faster than its main competitors, merge sort and heapsort. 417 | Quicksort is a divide-and-conquer algorithm. It works by selecting a 'pivot' element from the array and partitioning the other elements into two sub-arrays, according to whether they are less than or greater than the pivot. The sub-arrays are then sorted recursively. This can be done in-place, requiring small additional amounts of memory to perform the sorting. 418 | Quicksort is a comparison sort, meaning that it can sort items of any type for which a 'less-than' relation (formally, a total order) is defined. Efficient implementations of Quicksort are not a stable sort, meaning that the relative order of equal sort items is not preserved. 419 | Mathematical analysis of quicksort shows that, on average, the algorithm takes O[n log n] comparisons to sort n items. In the worst case, it makes O[n2] comparisons, though this behavior is rare. 420 | Best solutions can be available! 421 | Soon or later they will be available." 422 | 423 | my_str = my_str + my_str 424 | my_str = my_str + my_str 425 | 426 | //----- Text Area ----- 427 | tb := pdf.Box{ 428 | x: page.media_box.x / page.user_unit + 10 429 | y: 20 430 | w: page.media_box.w / page.user_unit - 20 431 | h: page.media_box.h / page.user_unit - 20 432 | } 433 | 434 | // justify align 435 | fnt_params.text_align = .justify 436 | mut tmp_txt := my_str 437 | mut tmp_res := false 438 | mut lo_txt := ' ' 439 | 440 | // set two columns 441 | boxes := [ 442 | pdf.Box{ 443 | x: tb.x 444 | y: tb.y 445 | w: tb.w / 2 - 10 446 | h: tb.h - 20 447 | }, 448 | pdf.Box{ 449 | x: tb.x + tb.w / 2 + 5 450 | y: tb.y 451 | w: tb.w / 2 - 10 452 | h: tb.h - 20 453 | }, 454 | ] 455 | 456 | for bx in boxes { 457 | if lo_txt.len > 0 { 458 | tmp_res, lo_txt, _ = page.text_box(tmp_txt, bx, fnt_params) 459 | if tmp_res { 460 | break 461 | } 462 | tmp_txt = lo_txt 463 | // println("leftover: [${lo_txt}]") 464 | } 465 | } 466 | // println("res: ${tmp_res} left_over: [${lo_txt}]") 467 | 468 | // render the PDF 469 | txt := doc.render()! 470 | 471 | // write it to a file 472 | os.write_file_array('example03.pdf', txt)! 473 | } 474 | ``` 475 | -------------------------------------------------------------------------------- /pdf.v: -------------------------------------------------------------------------------- 1 | module pdf 2 | 3 | /********************************************************************** 4 | * 5 | * pdf writer V implementation 6 | * 7 | * Copyright (c) 2020 Dario Deledda. All rights reserved. 8 | * Use of this source code is governed by an MIT license 9 | * that can be found in the LICENSE file. 10 | * 11 | * Note: 12 | * - All the internal values are treated as mm 13 | * 14 | * TODO: 15 | **********************************************************************/ 16 | import compress.zlib 17 | import strings 18 | import math 19 | // import os 20 | 21 | /****************************************************************************** 22 | * 23 | * General Utilities 24 | * 25 | ******************************************************************************/ 26 | 27 | /****************************************************************************** 28 | * 29 | * Obj Utilities 30 | * 31 | ******************************************************************************/ 32 | // pub // when in a module must be uncomented 33 | pub struct Obj { 34 | pub mut: 35 | id int // obj id 36 | ver int // version of the obj, default 0 37 | fields []string // list of the fields of the obj 38 | parts []string // list of all parts, these are before the txt field 39 | txt string // raw source content, it is used after the parts 40 | raw_data []u8 41 | 42 | is_stream bool // if true this object is a stream 43 | compress bool // if true the stream will be compressed 44 | 45 | name string 46 | } 47 | 48 | pub fn (o Obj) render_obj(mut res_c strings.Builder) !int { 49 | // creat the parts of the object 50 | mut tmp_txt := strings.new_builder(32768) 51 | 52 | // write raw binary data 53 | if o.raw_data.len > 0 { 54 | return o.render_obj_bytes(mut res_c) 55 | } else { 56 | for txt in o.parts { 57 | tmp_txt.write(txt.bytes())! 58 | } 59 | 60 | if o.compress { 61 | return o.render_obj_cmpr(mut res_c, tmp_txt.str()) 62 | } else { 63 | return o.render_obj_str(mut res_c, tmp_txt.str()) 64 | } 65 | } 66 | } 67 | 68 | fn (o Obj) render_obj_bytes(mut res_c strings.Builder) !int { 69 | // obj ids 70 | res_c.write('${o.id} ${o.ver} obj\n'.bytes())! 71 | 72 | // obj fields 73 | res_c.write('<< '.bytes())! 74 | for field in o.fields { 75 | res_c.write('${field} '.bytes())! 76 | } 77 | if o.txt.len > 0 { 78 | res_c.write('/Length ${o.raw_data.len}'.bytes())! 79 | } 80 | res_c.write(' >>\n'.bytes())! 81 | if o.is_stream { 82 | res_c.write('stream\n'.bytes())! 83 | } 84 | res_c.write(o.raw_data)! 85 | if o.is_stream { 86 | res_c.write('\nendstream\n'.bytes())! 87 | } 88 | // obj end 89 | res_c.write('endobj\n\n'.bytes())! 90 | return int(res_c.len) 91 | } 92 | 93 | fn (o Obj) render_obj_str(mut res_c strings.Builder, txt_parts string) !int { 94 | // obj ids 95 | res_c.write('${o.id} ${o.ver} obj\n'.bytes())! 96 | 97 | txt := o.txt + txt_parts 98 | 99 | // obj fields 100 | res_c.write('<< '.bytes())! 101 | for field in o.fields { 102 | res_c.write('${field} '.bytes())! 103 | } 104 | if o.txt.len > 0 { 105 | res_c.write('/Length1 ${txt.len}/Length ${txt.len}'.bytes())! 106 | } 107 | res_c.write(' >>\n'.bytes())! 108 | 109 | // content 110 | if o.txt.len > 0 { 111 | if o.is_stream { 112 | res_c.write('stream\n'.bytes())! 113 | } 114 | res_c.write(txt.bytes())! 115 | if o.is_stream { 116 | res_c.write('\nendstream'.bytes())! 117 | } 118 | res_c.write('\n'.bytes())! 119 | } 120 | 121 | // obj end 122 | res_c.write('endobj\n\n'.bytes())! 123 | return int(res_c.len) 124 | } 125 | 126 | fn (o Obj) render_obj_cmpr(mut res_c strings.Builder, txt_parts string) !int { 127 | // obj ids 128 | res_c.write('${o.id} ${o.ver} obj\n'.bytes())! 129 | 130 | // obj fields 131 | res_c.write('<< '.bytes())! 132 | for field in o.fields { 133 | res_c.write('${field} '.bytes())! 134 | } 135 | 136 | // cmp_status := C.compress(buf.data, &cmp_len, charptr(txt.str), u32(txt.len)) 137 | txt_buf := '${o.txt}${txt_parts}' 138 | buf := zlib.compress(txt_buf.bytes()) or { return error('compress failed') } 139 | 140 | // mandatory fields in a compress obj stream 141 | res_c.write('/Length1 ${txt_buf.len}'.bytes())! 142 | res_c.write('/Length ${buf.len}'.bytes())! 143 | res_c.write('/Filter/FlateDecode>>\n'.bytes())! 144 | res_c.write('stream\n'.bytes())! 145 | res_c.write(buf)! 146 | res_c.write('\nendstream\n'.bytes())! 147 | res_c.write('endobj\n'.bytes())! 148 | 149 | return int(res_c.len) 150 | } 151 | 152 | /****************************************************************************** 153 | * 154 | * Page struct 155 | * 156 | ******************************************************************************/ 157 | // default A4 page size 158 | pub struct Box { 159 | pub mut: 160 | x f32 161 | y f32 162 | w f32 = 595 163 | h f32 = 842 164 | } 165 | 166 | fn (box Box) str() string { 167 | return '${box.x} ${box.y} ${box.w} ${box.h}' 168 | } 169 | 170 | // all the formats are expressed in millimeters 171 | pub const page_fmt = { 172 | // ISO 173 | 'A0': Box{0, 0, 841, 1189} 174 | 'B0': Box{0, 0, 1000, 1414} 175 | 'A1': Box{0, 0, 594, 841} 176 | 'B1': Box{0, 0, 707, 1000} 177 | 'A2': Box{0, 0, 420, 594} 178 | 'B2': Box{0, 0, 500, 707} 179 | 'A3': Box{0, 0, 297, 420} 180 | 'B3': Box{0, 0, 353, 500} 181 | 'A4': Box{0, 0, 210, 297} 182 | 'B4': Box{0, 0, 250, 353} 183 | 'A5': Box{0, 0, 148, 210} 184 | 'B5': Box{0, 0, 176, 250} 185 | // american 186 | 'letter': Box{0, 0, 216, 279} 187 | 'legal': Box{0, 0, 216, 356} 188 | 'legal jr': Box{0, 0, 203, 127} 189 | 'tabloid': Box{0, 0, 279, 432} 190 | } 191 | 192 | pub const mm_unit = 2.83464 193 | pub const inch_unit = 72.0 194 | pub const pdf_unit = 1.0 // default for PDF manuals 195 | 196 | pub struct Page { 197 | pub mut: 198 | pdf &Pdf = &Pdf(unsafe { nil }) // PDF, page owner 199 | content_obj_index int = -1 // content object index 200 | page_obj_id int // obj id of the page 201 | obj_id_list []int // list of the page object 202 | resources []string // resource strings 203 | fields []string // additional fields for the page 204 | shaders []string // Pattern and Shaders 205 | // user unit in 1/72 of inch 206 | // 1/72 = 0.35278 mm 207 | // to achive a unit in mm specify 1.0/0.35278 = 2.83464 208 | // to achive uni in inch specify 72 209 | user_unit f32 = 1.0 210 | // attributes 211 | media_box Box = Box{} // page size 212 | crop_box Box = Box{} // default = media_box 213 | // draw variables 214 | border Box = Box{} 215 | } 216 | 217 | pub struct Page_params { 218 | pub mut: 219 | user_unit f32 = 2.83464 // default mm in 1/72 of inch 220 | format string = 'A4' // default fromat ISO A4 221 | media_box Box = Box{0, 0, 0, 0} // default a not valid bbox 222 | // content params 223 | is_stream bool = true 224 | compress bool 225 | // config param 226 | gen_content_obj bool 227 | } 228 | 229 | pub fn (mut p Pdf) create_page(params Page_params) int { 230 | // create a new object for the page 231 | obj := Obj{ 232 | id: p.get_new_id() 233 | } 234 | p.obj_list << obj 235 | 236 | // set the page format from the format table 237 | // if a format is not found check if the box is initatied 238 | // else default is ISO A4 239 | mut box := pdf.page_fmt['A4'] 240 | if params.format in pdf.page_fmt { 241 | // use a default format 242 | box = pdf.page_fmt[params.format] 243 | box.x *= pdf.mm_unit 244 | box.y *= pdf.mm_unit 245 | box.w *= pdf.mm_unit 246 | box.h *= pdf.mm_unit 247 | } else { 248 | // check if we have a valid media box 249 | sum := box.x + box.y + box.w + box.h 250 | if sum > 0.0 { 251 | box = params.media_box 252 | box.x *= params.user_unit 253 | box.y *= params.user_unit 254 | box.w *= params.user_unit 255 | box.h *= params.user_unit 256 | } 257 | } 258 | 259 | mut new_page := Page{ 260 | pdf: p 261 | page_obj_id: obj.id 262 | media_box: box 263 | crop_box: box 264 | user_unit: params.user_unit 265 | // draw variables 266 | border: box 267 | } 268 | 269 | // check user unit, otherwise use pdf default mm 270 | if new_page.user_unit == 0 { 271 | new_page.user_unit = p.user_unit 272 | } 273 | 274 | // if set generate a content object 275 | if params.gen_content_obj { 276 | // create the page Content 277 | mut content := Obj{ 278 | id: p.get_new_id() 279 | is_stream: params.is_stream 280 | compress: params.compress 281 | } 282 | 283 | // add the page Object to the PDF with its content 284 | p.obj_list << content 285 | new_page.content_obj_index = (p.obj_list.len - 1) 286 | new_page.obj_id_list << content.id 287 | } 288 | 289 | p.page_list << new_page 290 | return p.page_list.len - 1 291 | } 292 | 293 | pub fn (mut p Pdf) add_page_obj(mut pg Page, obj Obj) { 294 | p.obj_list << obj 295 | pg.obj_id_list << obj.id 296 | // println("len page list: ${pg.pdf.page_list.len}") 297 | } 298 | 299 | pub fn (mut p Page) push_content(txt string) bool { 300 | if p.content_obj_index >= 0 { 301 | p.pdf.obj_list[p.content_obj_index].txt += txt 302 | return true 303 | } 304 | return false 305 | } 306 | 307 | // set_unit set unit to use in the page 308 | pub fn (mut p Page) set_unit(in_unit string) { 309 | match in_unit { 310 | 'mm' { 311 | p.user_unit = 0.35278 312 | } 313 | 'cm' { 314 | p.user_unit = 3.5278 315 | } 316 | 'inch' { 317 | p.user_unit = 1.0 318 | } 319 | else { 320 | eprintln('unknown measurement unit') 321 | } 322 | } 323 | } 324 | 325 | // render_page render the page in the string builder and return the inital_displacement and page obj id 326 | pub fn (mut p Pdf) render_page(mut res_c strings.Builder, pg Page, parent_id int) !Posi { 327 | obj_id := p.get_obj_index_by_id(pg.page_obj_id) 328 | mut obj := p.obj_list[obj_id] 329 | obj.fields << '/Type /Page' 330 | obj.fields << '/Parent ${parent_id} 0 R' 331 | 332 | // obj.fields << "/UserUnit ${pg.user_unit}" // default 1/72 of inch 333 | obj.fields << '/MediaBox [ ${pg.media_box.str()} ]' 334 | obj.fields << '/CropBox [ ${pg.crop_box.str()} ]' 335 | 336 | for field in pg.fields { 337 | obj.fields << field 338 | } 339 | 340 | // resources 341 | obj.fields << '/Resources << /ProcSet [/PDF/ImageB/ImageC/ImageI/Text] ' 342 | // Color space 343 | obj.fields << '/Group<>' 344 | for rsrc in pg.resources { 345 | obj.fields << rsrc 346 | } 347 | 348 | /* 349 | // add the base fonts in use to each the page resources 350 | for _, x in p.base_font_used { 351 | obj.fields << '/Font << /F$x.font_name_id $x.obj_id 0 R >> ' 352 | } 353 | 354 | // add the TTF fonts 355 | for name, tf in p.ttf_font_used { 356 | obj.fields << '/Font << /$name ${tf.id_font} 0 R >> ' 357 | } 358 | */ 359 | 360 | // add the base fonts in use to each the page resources 361 | if p.base_font_used.len + p.ttf_font_used.len > 0 { 362 | mut txt := '/Font <<' 363 | // add the base fonts in use to each the page resources 364 | for _, x in p.base_font_used { 365 | txt += '/F${x.font_name_id} ${x.obj_id} 0 R ' 366 | } 367 | // add the TTF fonts 368 | for name, tf in p.ttf_font_used { 369 | txt += '/${name} ${tf.id_font} 0 R ' 370 | } 371 | 372 | txt += '>>' 373 | 374 | obj.fields << txt 375 | } 376 | 377 | //" /Shading << /Sh_${name} ${index} 0 R >> " 378 | // shaders 379 | if pg.shaders.len > 0 { 380 | obj.fields << '/Shading <<' 381 | for shader in pg.shaders { 382 | obj.fields << shader 383 | } 384 | obj.fields << '>> ' 385 | } 386 | 387 | obj.fields << '>> ' 388 | 389 | // Contents 390 | obj.fields << '/Contents ${pg.obj_id_list[0]} 0 R' 391 | 392 | // save displacement a obj id of the page 393 | posi := Posi{res_c.len, obj.id} 394 | 395 | obj.render_obj(mut res_c)! 396 | return posi 397 | } 398 | 399 | /****************************************************************************** 400 | * 401 | * PDF struct 402 | * 403 | ******************************************************************************/ 404 | // used to mantain the base font iformation in order to write the used resources for every page in the pdf 405 | struct BaseFontRsc { 406 | font_name_id int 407 | obj_id int 408 | } 409 | 410 | @[heap] 411 | pub struct Pdf { 412 | pub mut: 413 | obj_list []Obj = []Obj{} // list of all the object sof the pdf 414 | page_list []Page = []Page{} // list of all the pages struct, these are not the page Objects of the pdf 415 | base_font_used map[string]BaseFontRsc // contains all the base font used in the pdf 416 | ttf_font_used map[string]TtfFontRsc // contains all the ttf font used in the pdf 417 | id_count int // id used to count the added obj 418 | // utility data 419 | u_to_glyph_table map[string]string // map from unicode to postscritpp glyph 420 | user_unit f32 = 2.83464 // default mm in 1/72 of inch, inherit from pages if not specified 421 | } 422 | 423 | // init_afm_metrics init the unicode to postscritpp glyph map 424 | fn (mut p Pdf) init_afm_metrics() { 425 | for k, v in glyp_unicode { 426 | p.u_to_glyph_table[v.str()] = k 427 | } 428 | } 429 | 430 | pub fn (mut p Pdf) init() { 431 | // catalog id 1 432 | mut cat := Obj{ 433 | id: 1 434 | } 435 | cat.fields << '/Type /Catalog' 436 | cat.fields << '/Pages 2 0 R' 437 | // cat.fields << '/Metadata 3 0 R' 438 | // cat.fields << "/Outlines 3 0 R" 439 | p.obj_list << cat 440 | 441 | // page index id 2 442 | mut pindx := Obj{ 443 | id: 2 444 | } 445 | pindx.fields << '/Type /Pages' 446 | p.obj_list << pindx 447 | p.id_count = 2 448 | /* 449 | // Metadata 450 | mut metadata := Obj{ 451 | id: 3 452 | } 453 | 454 | metadata_str := ' 455 | 456 | 457 | 459 | 2022-07-04T23:09:34+02:00 460 | 461 | 462 | 463 | ' 464 | metadata.fields << '/Type /Metadata /Subtype /XML /Length ${metadata_str.len}' 465 | metadata.raw_data = metadata_str.bytes() 466 | metadata.is_stream = true 467 | p.obj_list << metadata 468 | p.id_count = 3 469 | */ 470 | /* 471 | // outlines id 3 472 | mut outlines := Obj{id:3} 473 | outlines.fields << "/Type /Outlines" 474 | outlines.fields << "/Count 0" 475 | p.obj_list << outlines 476 | 477 | p.id_count = 3 478 | */ 479 | // 480 | // Init the afm_metrics for glyph 481 | // 482 | p.init_afm_metrics() 483 | } 484 | 485 | // get_new_id generate a new id for the objects of the pdf, must be call to obtain a valid id for a new object 486 | pub fn (mut p Pdf) get_new_id() int { 487 | p.id_count++ 488 | return p.id_count 489 | } 490 | 491 | // get_obj_index_by_id retrive an object using its object id 492 | pub fn (p Pdf) get_obj_index_by_id(id int) int { 493 | for c, o in p.obj_list { 494 | if o.id == id { 495 | return c 496 | } 497 | } 498 | return -1 // not found 499 | } 500 | 501 | // get_obj_by_name retrive an object using its object name 502 | pub fn (p Pdf) get_obj_index_by_name(name string) int { 503 | for c, o in p.obj_list { 504 | if o.name == name { 505 | return c 506 | } 507 | } 508 | return -1 // not found 509 | } 510 | 511 | // utility struct, used to store the dispalcement of the objects in order to create the Xref table of the pdf 512 | pub struct Posi { 513 | pub mut: 514 | pos int 515 | id int 516 | } 517 | 518 | // render the pdf document to a string.Builder 519 | pub fn (mut p Pdf) render() !strings.Builder { 520 | mut posi := []Posi{} 521 | mut rendered := []int{} // rendered ids 522 | mut res := strings.new_builder(32768) 523 | res.write('%PDF-1.4\n'.bytes())! // format 524 | res.write('%äüöß\n\n'.bytes())! // format 525 | mut count := 1 526 | 527 | // catalog 528 | posi << Posi{res.len, count} 529 | rendered << count 530 | count++ 531 | // res.write(p.obj_list[0].render_obj(res)) 532 | p.obj_list[0].render_obj(mut res)! 533 | 534 | // Pages 535 | mut pl_obj := p.obj_list[1] 536 | mut page_list := strings.new_builder(128) 537 | for pg in p.page_list { 538 | page_list.write('${pg.page_obj_id} 0 R '.bytes())! 539 | } 540 | tmp_str := page_list.str() 541 | pl_obj.fields << ' /Kids[' 542 | pl_obj.fields << tmp_str 543 | pl_obj.fields << ']' 544 | pl_obj.fields << ' /Count ${p.page_list.len}' 545 | posi << Posi{res.len, count} 546 | rendered << count 547 | count++ 548 | // res.write(pl_obj.render_obj()) 549 | pl_obj.render_obj(mut res)! 550 | 551 | /* 552 | // outlines 553 | res.write(p.obj_list[2].render_obj()) 554 | rendered << 3 555 | */ 556 | 557 | // TTF Fonts 558 | for _, tf_rsc in p.ttf_font_used { 559 | // println("Rendering font [${k}]") 560 | 561 | posi << Posi{res.len, tf_rsc.id_font_file} 562 | rendered << tf_rsc.id_font_file 563 | render_ttf_files(mut res, tf_rsc) or { eprintln('Font file render failed!') } 564 | 565 | posi << Posi{res.len, tf_rsc.id_font} 566 | rendered << tf_rsc.id_font 567 | render_ttf_font(mut res, tf_rsc) or { eprintln('Font render failed!') } 568 | 569 | posi << Posi{res.len, tf_rsc.id_font_desc} 570 | rendered << tf_rsc.id_font_desc 571 | render_ttf_font_decriptor(mut res, tf_rsc) or { eprintln('Font descriptor render failed!') } 572 | } 573 | 574 | // render pages 575 | for pg in p.page_list { 576 | posi_tmp := p.render_page(mut res, pg, pl_obj.id)! 577 | 578 | // save the byte displacement of the page we rendered 579 | posi << posi_tmp 580 | rendered << posi_tmp.id 581 | } 582 | 583 | // render all other objs 584 | for obj in p.obj_list { 585 | if obj.id in rendered { 586 | continue 587 | } 588 | posi << Posi{res.len, obj.id} 589 | rendered << obj.id 590 | obj.render_obj(mut res)! 591 | } 592 | 593 | // render xref 594 | // TODO: do a better dorting, now it is very scarce!! 595 | start_xref := res.len 596 | res.write('xref\n'.bytes())! 597 | res.write('0 1\n'.bytes())! 598 | // res.write('0 ${posi.len + 1}\n'.bytes())! 599 | res.write('0000000000 65535 f \n'.bytes())! 600 | 601 | mut ids := posi.map(int(it.id)) 602 | ids.sort() 603 | 604 | for x in ids { 605 | for row in posi { 606 | if row.id == x { 607 | res.write('${row.id} 1\n'.bytes())! 608 | res.write('${row.pos:010d} 00000 n \n'.bytes())! 609 | // res.write('${row.pos:010d} 00000 n\r\n'.bytes())! 610 | break 611 | } 612 | } 613 | } 614 | 615 | // trailer 616 | res.write('trailer\n'.bytes())! 617 | res.write('< <00000000000000000000000000000000>]>>\n'.bytes())! 618 | 619 | res.write('startxref\n'.bytes())! 620 | res.write(start_xref.str().bytes())! 621 | res.write('\n%%EOF\n'.bytes())! 622 | return res 623 | } 624 | 625 | /****************************************************************************** 626 | * 627 | * Color 628 | * 629 | ******************************************************************************/ 630 | pub struct RGB { 631 | pub mut: 632 | r f32 633 | g f32 634 | b f32 635 | } 636 | 637 | /****************************************************************************** 638 | * 639 | * Base Font resources 640 | * 641 | ******************************************************************************/ 642 | // get_base_font_list retrive the list of usable base fonts in a pdf 643 | pub fn get_base_font_list() []string { 644 | return base_font_params.keys() 645 | } 646 | 647 | // use_base_font specify a base font that must be used in the pdf 648 | pub fn (mut p Pdf) use_base_font(font_name string) bool { 649 | if font_name in base_font_params.keys() { 650 | // add default PDF font, no need to include it 651 | mut font_obj := Obj{ 652 | id: p.get_new_id() 653 | } 654 | 655 | // for now we use the array position as index 656 | font_name_id := p.base_font_used.len 657 | 658 | font_obj.fields << '/Name /F${font_name_id} /Type /Font /Subtype /Type1 /BaseFont /${font_name} /Encoding /MacRomanEncoding' 659 | p.obj_list << font_obj 660 | p.base_font_used[font_name] = BaseFontRsc{ 661 | font_name_id: font_name_id 662 | obj_id: font_obj.id 663 | } 664 | return true 665 | } 666 | return false 667 | } 668 | 669 | pub fn (mut p Pdf) get_base_font_id(font_name string) string { 670 | mut res := '' 671 | if font_name in p.base_font_used { 672 | res = 'F${p.base_font_used[font_name].font_name_id}' 673 | } 674 | return res 675 | } 676 | 677 | /****************************************************************************** 678 | * 679 | * JPEG resources 680 | * 681 | ******************************************************************************/ 682 | // get_jpeg_info get the width,height and number of bit per pixel of a jpeg file 683 | pub fn get_jpeg_info(data []u8) (int, int, int) { 684 | // cehck for empty 685 | if data.len <= 0 { 686 | return 0, 0, 0 687 | } 688 | 689 | mut i := 0 690 | for i < (data.len - 1) { 691 | if data[i] == 0xff && (data[i + 1] == 0xc0 || data[i + 1] == 0xc2) { 692 | i += 2 693 | // length := (int(data[i]) << 8) | int(data[i + 1]) 694 | i += 2 695 | n_bit := data[i] 696 | i++ 697 | height := (u16(data[i]) << 8 | data[i + 1]) 698 | i += 2 699 | width := (u16(data[i]) << 8 | data[i + 1]) 700 | i += 2 701 | // println("get jped params l: $length nbit: $n_bit w:[$width] h:[$height].") 702 | return n_bit, width, height 703 | } 704 | i++ 705 | } 706 | return 0, 0, 0 707 | } 708 | 709 | // add_jpeg_resource add a jpeg as resource to the pdf 710 | pub fn (mut p Pdf) add_jpeg_resource(jpeg_data []u8) int { 711 | jpg_n_bit, jpg_w, jpg_h := get_jpeg_info(jpeg_data) 712 | mut jpeg_obj := Obj{ 713 | id: p.get_new_id() 714 | is_stream: true 715 | } 716 | jpeg_obj.fields << '/Type/XObject/Subtype/Image /Width ${jpg_w} /Height ${jpg_h} /BitsPerComponent ${jpg_n_bit} /ColorSpace/DeviceRGB/Filter/DCTDecode/Length ${jpeg_data.len}' 717 | jpeg_obj.raw_data = jpeg_data 718 | p.obj_list << jpeg_obj 719 | return jpeg_obj.id 720 | } 721 | 722 | // use_jpeg specify that the jpeg with jpeg_id is used in the page 723 | pub fn (mut p Page) use_jpeg(jpeg_id int) { 724 | p.resources << '/XObject<>' 725 | } 726 | 727 | pub fn (mut pg Page) draw_jpeg(jpeg_id int, bx Box) string { 728 | x := bx.x * pg.user_unit 729 | y := pg.media_box.h - (bx.y * pg.user_unit) 730 | w := bx.w * pg.user_unit 731 | h := bx.h * pg.user_unit 732 | 733 | return ' 734 | q 735 | ${w} 0 0 ${h} ${x} ${y} cm 736 | /Image${jpeg_id} Do 737 | Q 738 | ' 739 | } 740 | 741 | /****************************************************************************** 742 | * 743 | * Text 744 | * 745 | ******************************************************************************/ 746 | pub enum Text_align { 747 | left 748 | center 749 | right 750 | justify 751 | } 752 | 753 | pub struct Text_params { 754 | pub mut: 755 | font_size f32 = 12.0 756 | font_name string 757 | font_color_c string 758 | font_color_f string 759 | render_mode int = -1 760 | word_spacing f32 761 | leading f32 = 0.1 // in proportion of the font size 762 | // transformation matrix 763 | tm00 f32 764 | tm10 f32 765 | tm01 f32 766 | tm11 f32 767 | // text allign 768 | text_align Text_align = .left 769 | // color 770 | s_color RGB = RGB{-1, 0, 0} 771 | f_color RGB = RGB{-1, 0, 0} 772 | } 773 | 774 | // return the increment of Y for next line 775 | pub fn (mut pg Page) new_line_offset(fnt_params Text_params) f32 { 776 | return f32(fnt_params.font_size + fnt_params.font_size * fnt_params.leading) / pg.user_unit 777 | } 778 | 779 | // clean_string clean form round brackets and backslash for PDF string standard 780 | pub fn clean_pdf_string(txt string) string { 781 | return txt.replace_each(['(', '\\(', ')', '\\)', '\\', '\\\\']) 782 | } 783 | 784 | pub fn (mut tp Text_params) scale(x_scale f32, y_scale f32) { 785 | tp.tm00 = x_scale 786 | tp.tm11 = y_scale 787 | } 788 | 789 | fn (pg Page) get_text_parms(x f32, y f32, params Text_params) (string, string, string, string, string, string) { 790 | x1 := x * pg.user_unit 791 | y1 := pg.media_box.h - (y * pg.user_unit) 792 | 793 | mut font_id := 'F1' 794 | if params.font_name in pg.pdf.ttf_font_used { 795 | font_id = params.font_name 796 | } else { 797 | font_id = 'F${pg.pdf.base_font_used[params.font_name].font_name_id}' 798 | } 799 | 800 | redender_mode := if params.render_mode >= 0 { '${params.render_mode} Tr\n' } else { '' } 801 | word_spacing := if params.word_spacing > 0 { 802 | '${params.word_spacing * pg.user_unit} Tw\n' 803 | } else { 804 | '' 805 | } 806 | txt_matrix := if params.tm00 != 0.0 { 807 | '${params.tm00} ${params.tm01} ${params.tm10} ${params.tm11} ${x1} ${y1} Tm\n' 808 | } else { 809 | '${x1} ${y1} Td\n' 810 | } 811 | 812 | stroke_color := if params.s_color.r < 0 { 813 | '' 814 | } else { 815 | '${params.s_color.r} ${params.s_color.g} ${params.s_color.b} RG ' 816 | } 817 | fill_color := if params.f_color.r < 0 { 818 | '' 819 | } else { 820 | '${params.f_color.r} ${params.f_color.g} ${params.f_color.b} rg ' 821 | } 822 | 823 | return font_id, redender_mode, word_spacing, txt_matrix, stroke_color, fill_color 824 | } 825 | 826 | // draw_raw_text draw a simple raw string at the x,y coordinates with the text parameters params. 827 | // The string is passed directly to the 'TJ' pdf command without filtering. 828 | // To draw a simple string you must write it (your text) with round brackets around the text. 829 | // For further information have a look af the PDF standard for TJ command 830 | pub fn (pg Page) draw_raw_text(in_txt string, x f32, y f32, params Text_params) string { 831 | font_id, redender_mode, word_spacing, txt_matrix, stroke_color, fill_color := pg.get_text_parms(x, 832 | y, params) 833 | 834 | return ' 835 | BT 836 | /${font_id} ${params.font_size} Tf 837 | ${stroke_color}${fill_color}${txt_matrix}${redender_mode}${word_spacing}[${in_txt}]TJ 838 | ET 839 | ' 840 | } 841 | 842 | // draw_base_text draw a simple string at the x,y coordinates with the text parameters params 843 | // optional you can use PDF Literal string 3-byte UTF-8 BOM: (\357\273\277 ... ) 844 | pub fn (pg Page) draw_base_text(in_txt string, x f32, y f32, params Text_params) string { 845 | return pg.draw_raw_text('(${clean_pdf_string(in_txt)})', x, y, params) 846 | } 847 | 848 | // draw_unicode_text draw a simple raw string at the x,y coordinates with the text parameters params. 849 | // The string is composed by the bytes of the utf8 chars: 850 | // '€ABC' must be written as the following string '80 41 42 43' 851 | // The conversion in sequence of bystes as string is up to the user 852 | pub fn (pg Page) draw_unicode_text(in_txt string, x f32, y f32, params Text_params) string { 853 | mut res := strings.new_builder(in_txt.len * 3 + 2) 854 | 855 | res.write_string('<') 856 | for byte_val in in_txt.bytes() { 857 | res.write_string(' ${byte_val:02X}') 858 | } 859 | res.write_string(' >') 860 | return pg.draw_raw_text(res.str(), x, y, params) 861 | } 862 | 863 | // calc_word_spacing calculate the sapcing to add to the space char 0x20 to fill the row only if txt fill al least half of teh horizontal space of the box.w 864 | pub fn (pg Page) calc_word_spacing(txt string, in_box Box, in_params Text_params) f32 { 865 | mut params := in_params 866 | mut tmp_width, _, _ := pg.calc_string_bb(txt, in_params) 867 | 868 | // if the width is less then the half do not calculate space 869 | if tmp_width <= (in_box.w * 0.75) { 870 | return params.word_spacing 871 | } 872 | 873 | n_space := txt.count(' ') 874 | if n_space <= 0 { 875 | return params.word_spacing 876 | } 877 | return (in_box.w - tmp_width) / n_space 878 | } 879 | 880 | // text_box draw a text inside a box, return true if the text fit in the box, otherwise false and the leftover text and the last used y coordinate 881 | pub fn (mut pg Page) text_box(txt string, in_box Box, in_params Text_params) (bool, string, f32) { 882 | mut params := in_params 883 | 884 | mut box := Box{in_box.x, in_box.y, in_box.w, in_box.h} 885 | row_height := (params.font_size + params.font_size * params.leading) / pg.user_unit 886 | 887 | // draw bb 888 | // pg.push_content("1.0 0.0 0.0 RG\n") 889 | // pg.push_content(pg.draw_rect(box)) 890 | 891 | // align flag multiplier 892 | right_align := if params.text_align == .right { 1 } else { 0 } 893 | center_align := if params.text_align == .center { 1 } else { 0 } 894 | 895 | mut y := box.y + row_height 896 | rows := txt.split_into_lines() 897 | for c, row in rows { 898 | mut tmp_row := row 899 | 900 | // skip empty rows 901 | if tmp_row.trim_space().len < 2 { 902 | y += row_height 903 | continue 904 | } 905 | 906 | mut tmp_width, _, _ := pg.calc_string_bb(tmp_row, params) 907 | 908 | // the row is shorter than the box width 909 | if tmp_width <= box.w { 910 | if params.text_align == .justify { 911 | tmp_ws := params.word_spacing 912 | params.word_spacing = pg.calc_word_spacing(tmp_row, box, params) 913 | pg.push_content(pg.draw_base_text(tmp_row, box.x, y, params)) 914 | params.word_spacing = tmp_ws 915 | } else { 916 | pg.push_content(pg.draw_base_text(tmp_row, box.x + 917 | (box.w - tmp_width) * right_align + center_align * (box.w - tmp_width) * 0.5, 918 | y, params)) 919 | } 920 | y += row_height 921 | if y > (box.y + box.h) { 922 | if c + 1 == rows.len { 923 | pg.push_content('0 Tw\n') 924 | return true, '', y 925 | } 926 | // println("Too much text! [FL]") 927 | leftover_text := rows[c + 1..].join('\n') 928 | pg.push_content('0 Tw\n') 929 | return false, leftover_text, y 930 | } 931 | 932 | // the row is longer than the box width, we need to cut it 933 | } else { 934 | mut words_list := row.split(' ') 935 | mut l := words_list.len 936 | for l > 0 { 937 | tmp_txt := words_list[..l].join(' ').trim_space() 938 | tmp_width, _, _ = pg.calc_string_bb(tmp_txt, params) 939 | if tmp_width <= box.w { 940 | if params.text_align == .justify { 941 | tmp_ws := params.word_spacing 942 | params.word_spacing = pg.calc_word_spacing(tmp_txt, box, params) 943 | pg.push_content(pg.draw_base_text(tmp_txt, box.x, y, params)) 944 | params.word_spacing = tmp_ws 945 | } else { 946 | pg.push_content(pg.draw_base_text(tmp_txt, box.x + 947 | (box.w - tmp_width) * right_align + 948 | center_align * (box.w - tmp_width) * 0.5, y, params)) 949 | } 950 | y += row_height 951 | if y > (box.y + box.h) { 952 | if c + 1 == rows.len { 953 | pg.push_content('0 Tw\n') 954 | return true, '', y 955 | } 956 | // println("Too much text! [CL]") 957 | leftover_text := words_list[l..].join(' ').trim_space() + '\n' + rows[c + 958 | 1..].join('\n') 959 | pg.push_content('0 Tw\n') 960 | return false, leftover_text, y 961 | } 962 | words_list = unsafe { words_list[l..] } 963 | l = words_list.len 964 | continue 965 | } 966 | l-- 967 | } 968 | } 969 | } 970 | // all the text fitted 971 | pg.push_content('0 Tw\n') 972 | return true, '', y 973 | } 974 | 975 | /****************************************************************************** 976 | * 977 | * Draw graphic 978 | * 979 | ******************************************************************************/ 980 | pub fn (pg Page) draw_rect(b Box) string { 981 | // box coordinates transformation 982 | x0 := (b.x * pg.user_unit) 983 | x1 := x0 + b.w * pg.user_unit 984 | y0 := pg.media_box.h - b.y * pg.user_unit 985 | y1 := y0 - b.h * pg.user_unit 986 | 987 | rect_txt := ' 988 | ${x0} ${y0} m 989 | ${x1} ${y0} l 990 | ${x1} ${y1} l 991 | ${x0} ${y1} l 992 | ${x0} ${y0} l 993 | S 994 | ' 995 | return rect_txt 996 | } 997 | 998 | pub fn (pg Page) draw_filled_rect(b Box) string { 999 | // box coordinates transformation 1000 | x0 := (b.x * pg.user_unit) 1001 | x1 := x0 + b.w * pg.user_unit 1002 | y0 := pg.media_box.h - b.y * pg.user_unit 1003 | y1 := y0 - b.h * pg.user_unit 1004 | 1005 | rect_txt := ' 1006 | ${x0} ${y0} m 1007 | ${x1} ${y0} l 1008 | ${x1} ${y1} l 1009 | ${x0} ${y1} l 1010 | ${x0} ${y0} l 1011 | f 1012 | S 1013 | ' 1014 | return rect_txt 1015 | } 1016 | 1017 | /****************************************************************************** 1018 | * 1019 | * Pattern: axis Shader 1020 | * 1021 | ******************************************************************************/ 1022 | /* 1023 | 2 0 obj 1024 | << 1025 | /ProcSet [/PDF /Text /ImageB /ImageC /ImageI] 1026 | /Font << /F1 3 0 R /F2 4 0 R >> 1027 | /XObject << /XT5 5 0 R /I0 11 0 R >> 1028 | /Pattern << /p1 15 0 R /p2 19 0 R /p3 21 0 R /p4 23 0 R /p5 25 0 R >> 1029 | /Shading << /Sh1 14 0 R /Sh2 18 0 R /Sh3 20 0 R /Sh4 22 0 R /Sh5 24 0 R >> >> 1030 | endobj 1031 | 1032 | // shader axial gradient 1033 | 12 0 obj 1034 | << /FunctionType 3 /Domain [0 1] /Functions [13 0 R] /Bounds [] /Encode [0 1] >> 1035 | endobj 1036 | 13 0 obj 1037 | << /FunctionType 2 /Domain [0 1] /C0 [1.000000 0.000000 0.000000] /C1 [0.000000 0.000000 0.784314] /N 1 >> 1038 | endobj 1039 | 14 0 obj 1040 | << /ShadingType 2 /ColorSpace /DeviceRGB /Coords [0.000000 0.000000 1.000000 0.000000] /Domain [0 1] /Function 12 0 R /Extend [true true] >> 1041 | endobj 1042 | 15 0 obj 1043 | << /Type /Pattern /PatternType 2 /Shading 14 0 R >> 1044 | endobj 1045 | */ 1046 | 1047 | pub fn (mut p Pdf) create_linear_gradient_shader(name string, c1 RGB, c2 RGB, angle f32) int { 1048 | // FunctionType 2 1049 | mut f2_obj := Obj{ 1050 | id: p.get_new_id() 1051 | is_stream: false 1052 | } 1053 | f2_obj.fields << ' /FunctionType 2 /Domain [0 1] /C0 [${c1.r} ${c1.g} ${c1.b}] /C1 [${c2.r} ${c2.g} ${c2.b}] /N 1 ' 1054 | p.obj_list << f2_obj 1055 | 1056 | // FunctionType 3 1057 | mut f3_obj := Obj{ 1058 | id: p.get_new_id() 1059 | is_stream: false 1060 | } 1061 | f3_obj.fields << ' /FunctionType 3 /Domain [0 1] /Functions [${f2_obj.id} 0 R] /Bounds [] /Encode [0 1] ' 1062 | p.obj_list << f3_obj 1063 | 1064 | // ShadingType 2 1065 | mut s2_obj := Obj{ 1066 | id: p.get_new_id() 1067 | is_stream: false 1068 | } 1069 | /* 1070 | s2_obj.fields << " /ShadingType 2 /ColorSpace /DeviceRGB /Coords [0.000000 0.000000 1.000000 0.000000] /Domain [0 1] /Function 1071 | ${f3_obj.id} 0 R /Extend [true true] " 1072 | */ 1073 | 1074 | // rotation angle of the gradient 1075 | grad_sn := math.sin(angle) 1076 | grad_cs := math.cos(angle) 1077 | s2_obj.fields << ' /ShadingType 2 /ColorSpace /DeviceRGB /Coords [0.000000 0.000000 ${grad_cs} ${grad_sn}] /Domain [0 1] /Function 1078 | ${f3_obj.id} 0 R /Extend [true true] ' 1079 | p.obj_list << s2_obj 1080 | 1081 | // shader obj 1082 | mut shader_obj := Obj{ 1083 | id: p.get_new_id() 1084 | is_stream: false 1085 | } 1086 | shader_obj.fields << ' /Type /Pattern /PatternType 2 /Shading ${s2_obj.id} 0 R ' 1087 | shader_obj.name = name 1088 | p.obj_list << shader_obj 1089 | return shader_obj.id 1090 | } 1091 | 1092 | pub fn (mut pg Page) use_shader(name string) bool { 1093 | index := pg.pdf.get_obj_index_by_name(name) 1094 | if index >= 0 { 1095 | pg.shaders << '/Sh_${name} ${index} 0 R ' 1096 | // pg.resources << " /Shading << /Sh_${name} ${index} 0 R >> " 1097 | return true 1098 | } 1099 | return false 1100 | } 1101 | 1102 | /* 1103 | * q % save state 1104 | * ${x} ${y} ${w} ${-h} re % clip Path 1105 | * W % clip command 1106 | * n % end the path without stroke 1107 | * % Modify the current transformation matrix 1108 | * ${grad_len} 0 0 ${grad_len} ${x} ${y} cm 1109 | * /Sh_grad sh % Shader name to use for paint 1110 | * Q % restore state 1111 | */ 1112 | pub fn (mut pg Page) draw_gradient_box(name string, b Box, in_grad_len f32) string { 1113 | // box coordinates transformation 1114 | x := (b.x * pg.user_unit) 1115 | w := b.w * pg.user_unit 1116 | y := pg.media_box.h - b.y * pg.user_unit 1117 | h := b.h * pg.user_unit 1118 | grad_len := in_grad_len * pg.user_unit 1119 | return ' 1120 | q 1121 | ${x} ${y} ${w} ${-h} re W n 1122 | ${grad_len} 0 0 ${grad_len} ${x} ${y} cm 1123 | /Sh_${name} sh 1124 | Q 1125 | ' 1126 | } 1127 | 1128 | /****************************************************************************** 1129 | * 1130 | * Utility 1131 | * 1132 | ******************************************************************************/ 1133 | // utf8util_char_len calculate the length in bytes of a utf8 char 1134 | fn utf8util_char_len(b u8) int { 1135 | return ((0xe5000000 >> ((b >> 3) & 0x1e)) & 3) + 1 1136 | } 1137 | 1138 | // get_uchar convert a unicode glyph in string[index] into a int unicode char and return the couple (char,len) 1139 | fn get_uchar(s string, index int) (int, int) { 1140 | mut res := 0 1141 | mut ch_len := 0 1142 | unsafe { 1143 | if s.len > 0 { 1144 | ch_len = utf8util_char_len(s.str[index]) 1145 | 1146 | if ch_len == 1 { 1147 | return u16(s.str[index]), 1 1148 | } 1149 | if ch_len > 1 && ch_len < 5 { 1150 | mut lword := u16(0) 1151 | for i := 0; i < ch_len; i++ { 1152 | lword = (lword << 8 | s.str[index + i]) 1153 | } 1154 | 1155 | // 2 byte utf-8 1156 | // byte format: 110xxxxx 10xxxxxx 1157 | // 1158 | if ch_len == 2 { 1159 | res = (lword & 0x1f00) >> 2 | (lword & 0x3f) 1160 | } 1161 | // 3 byte utf-8 1162 | // byte format: 1110xxxx 10xxxxxx 10xxxxxx 1163 | // 1164 | else if ch_len == 3 { 1165 | res = (lword & 0x0f0000) >> 4 | (lword & 0x3f00) >> 2 | (lword & 0x3f) 1166 | } 1167 | // 4 byte utf-8 1168 | // byte format: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 1169 | // 1170 | else if ch_len == 4 { 1171 | res = ((lword & 0x07000000) >> 6) | ((lword & 0x003f0000) >> 4) | ((lword & 0x00003F00) >> 2) | (lword & 0x0000003f) 1172 | } 1173 | } 1174 | } 1175 | } 1176 | return res, ch_len 1177 | } 1178 | 1179 | // calc_string_bb get a string and a Text_params struct and return his base bounding box (width,y_min,y_max) 1180 | pub fn (pg Page) calc_string_bb(txt string, params Text_params) (f32, f32, f32) { 1181 | mut w := f32(0) 1182 | mut w_s := f32(0) 1183 | mut index := 0 1184 | mut space_scale := if params.word_spacing > 0.0 { 1185 | f32(params.word_spacing * pg.user_unit) 1186 | } else { 1187 | f32(0.0) 1188 | } 1189 | 1190 | mult := (params.font_size) / (1000.0 * pg.user_unit) 1191 | 1192 | // println("space_scale: ${space_scale}") 1193 | mut width := f32(0) 1194 | mut ascender := f32(0) 1195 | mut descender := f32(0) 1196 | if params.font_name in pg.pdf.ttf_font_used { 1197 | ft_rsc := pg.pdf.ttf_font_used[params.font_name] 1198 | for { 1199 | ch, len := get_uchar(txt, index) 1200 | w_index := ch - ft_rsc.first_char 1201 | mut w_glyph := 0 1202 | if w_index >= 0 && w_index < ft_rsc.widths.len { 1203 | // println("Found: ${ch:c} => ${ft_rsc.widths[w_index]}") 1204 | w_glyph = ft_rsc.widths[w_index] 1205 | } else { 1206 | // println("Not found: ${ch:c}") 1207 | } 1208 | w += w_glyph 1209 | // manage space_scale for the space char 1210 | if len == 1 && ch == 0x20 { 1211 | w_s += space_scale 1212 | } 1213 | index += len 1214 | if index > txt.len { 1215 | break 1216 | } 1217 | } 1218 | 1219 | ascender = f32(ft_rsc.ascent) * mult 1220 | descender = f32(ft_rsc.descent) * mult 1221 | } else { 1222 | for { 1223 | ch, len := get_uchar(txt, index) 1224 | glyph := pg.pdf.u_to_glyph_table[ch.str()] 1225 | w_glyph := base_font_metrics[params.font_name][glyph] 1226 | // println("$ch $len $glyph [$w_glyph]") 1227 | w += w_glyph 1228 | // manage space_scale for the space char 1229 | if len == 1 && ch == 0x20 { 1230 | w_s += space_scale 1231 | } 1232 | index += len 1233 | if index > txt.len { 1234 | break 1235 | } 1236 | } 1237 | ascender = f32(base_font_params[params.font_name]['Ascender']) * mult 1238 | descender = f32(base_font_params[params.font_name]['Descender']) * mult 1239 | } 1240 | 1241 | width = f32(w) * mult + (w_s / pg.user_unit) 1242 | 1243 | return width, ascender, descender 1244 | } 1245 | 1246 | // 1247 | // state save and restore 1248 | // 1249 | pub fn (mut pg Page) push_state() string { 1250 | return 'q\n' 1251 | } 1252 | 1253 | pub fn (mut pg Page) pop_state() string { 1254 | return 'Q\n' 1255 | } 1256 | 1257 | /****************************************************************************** 1258 | * 1259 | * Main 1260 | * 1261 | ******************************************************************************/ 1262 | /* 1263 | fn main1(){ 1264 | txt := "BT /F1 14 Tf 10 10 Td (Hello World2) Tj ET" 1265 | cmp_len := C.compressBound(txt.len) 1266 | println("txt.len: ${txt.len} cmp_len:${cmp_len}") 1267 | mut buf := malloc(int(cmp_len)) 1268 | 1269 | cmp_status := C.compress(buf, &cmp_len, charptr(txt.str), u32(txt.len)) 1270 | println(cmp_status) 1271 | if cmp_status != C.MZ_OK { 1272 | println("Failed!") 1273 | free(buf) 1274 | } 1275 | 1276 | mut res := strings.new_builder(2048) 1277 | res.write("<> >>\nstream\n") 1278 | res.write_bytes(buf,int(cmp_len)) 1279 | res.write("\nendstream") 1280 | println(res.str()) 1281 | 1282 | } 1283 | */ 1284 | -------------------------------------------------------------------------------- /afm_metrics.v: -------------------------------------------------------------------------------- 1 | module pdf 2 | 3 | const base_font_params = { 4 | 'Courier-Bold': { 5 | 'Ascender': 629 6 | 'Descender': -157 7 | } 8 | 'Courier-BoldOblique': { 9 | 'Ascender': 629 10 | 'Descender': -157 11 | } 12 | 'Courier-Oblique': { 13 | 'Ascender': 629 14 | 'Descender': -157 15 | } 16 | 'Courier': { 17 | 'Ascender': 629 18 | 'Descender': -157 19 | } 20 | 'Helvetica-Bold': { 21 | 'Ascender': 718 22 | 'Descender': -207 23 | } 24 | 'Helvetica-BoldOblique': { 25 | 'Ascender': 718 26 | 'Descender': -207 27 | } 28 | 'Helvetica-Oblique': { 29 | 'Ascender': 718 30 | 'Descender': -207 31 | } 32 | 'Helvetica': { 33 | 'Ascender': 718 34 | 'Descender': -207 35 | } 36 | 'Symbol': { 37 | 'Ascender': 0 38 | 'Descender': 0 39 | } 40 | 'Times-Bold': { 41 | 'Ascender': 683 42 | 'Descender': -217 43 | } 44 | 'Times-BoldItalic': { 45 | 'Ascender': 683 46 | 'Descender': -217 47 | } 48 | 'Times-Italic': { 49 | 'Ascender': 683 50 | 'Descender': -217 51 | } 52 | 'Times-Roman': { 53 | 'Ascender': 683 54 | 'Descender': -217 55 | } 56 | 'ZapfDingbats': { 57 | 'Ascender': 0 58 | 'Descender': 0 59 | } 60 | } 61 | const base_font_metrics = { 62 | 'Courier-Bold': { 63 | 'space': 600 64 | 'exclam': 600 65 | 'quotedbl': 600 66 | 'numbersign': 600 67 | 'dollar': 600 68 | 'percent': 600 69 | 'ampersand': 600 70 | 'quoteright': 600 71 | 'parenleft': 600 72 | 'parenright': 600 73 | 'asterisk': 600 74 | 'plus': 600 75 | 'comma': 600 76 | 'hyphen': 600 77 | 'period': 600 78 | 'slash': 600 79 | 'zero': 600 80 | 'one': 600 81 | 'two': 600 82 | 'three': 600 83 | 'four': 600 84 | 'five': 600 85 | 'six': 600 86 | 'seven': 600 87 | 'eight': 600 88 | 'nine': 600 89 | 'colon': 600 90 | 'semicolon': 600 91 | 'less': 600 92 | 'equal': 600 93 | 'greater': 600 94 | 'question': 600 95 | 'at': 600 96 | 'A': 600 97 | 'B': 600 98 | 'C': 600 99 | 'D': 600 100 | 'E': 600 101 | 'F': 600 102 | 'G': 600 103 | 'H': 600 104 | 'I': 600 105 | 'J': 600 106 | 'K': 600 107 | 'L': 600 108 | 'M': 600 109 | 'N': 600 110 | 'O': 600 111 | 'P': 600 112 | 'Q': 600 113 | 'R': 600 114 | 'S': 600 115 | 'T': 600 116 | 'U': 600 117 | 'V': 600 118 | 'W': 600 119 | 'X': 600 120 | 'Y': 600 121 | 'Z': 600 122 | 'bracketleft': 600 123 | 'backslash': 600 124 | 'bracketright': 600 125 | 'asciicircum': 600 126 | 'underscore': 600 127 | 'quoteleft': 600 128 | 'a': 600 129 | 'b': 600 130 | 'c': 600 131 | 'd': 600 132 | 'e': 600 133 | 'f': 600 134 | 'g': 600 135 | 'h': 600 136 | 'i': 600 137 | 'j': 600 138 | 'k': 600 139 | 'l': 600 140 | 'm': 600 141 | 'n': 600 142 | 'o': 600 143 | 'p': 600 144 | 'q': 600 145 | 'r': 600 146 | 's': 600 147 | 't': 600 148 | 'u': 600 149 | 'v': 600 150 | 'w': 600 151 | 'x': 600 152 | 'y': 600 153 | 'z': 600 154 | 'braceleft': 600 155 | 'bar': 600 156 | 'braceright': 600 157 | 'asciitilde': 600 158 | 'exclamdown': 600 159 | 'cent': 600 160 | 'sterling': 600 161 | 'fraction': 600 162 | 'yen': 600 163 | 'florin': 600 164 | 'section': 600 165 | 'currency': 600 166 | 'quotesingle': 600 167 | 'quotedblleft': 600 168 | 'guillemotleft': 600 169 | 'guilsinglleft': 600 170 | 'guilsinglright': 600 171 | 'fi': 600 172 | 'fl': 600 173 | 'endash': 600 174 | 'dagger': 600 175 | 'daggerdbl': 600 176 | 'periodcentered': 600 177 | 'paragraph': 600 178 | 'bullet': 600 179 | 'quotesinglbase': 600 180 | 'quotedblbase': 600 181 | 'quotedblright': 600 182 | 'guillemotright': 600 183 | 'ellipsis': 600 184 | 'perthousand': 600 185 | 'questiondown': 600 186 | 'grave': 600 187 | 'acute': 600 188 | 'circumflex': 600 189 | 'tilde': 600 190 | 'macron': 600 191 | 'breve': 600 192 | 'dotaccent': 600 193 | 'dieresis': 600 194 | 'ring': 600 195 | 'cedilla': 600 196 | 'hungarumlaut': 600 197 | 'ogonek': 600 198 | 'caron': 600 199 | 'emdash': 600 200 | 'AE': 600 201 | 'ordfeminine': 600 202 | 'Lslash': 600 203 | 'Oslash': 600 204 | 'OE': 600 205 | 'ordmasculine': 600 206 | 'ae': 600 207 | 'dotlessi': 600 208 | 'lslash': 600 209 | 'oslash': 600 210 | 'oe': 600 211 | 'germandbls': 600 212 | 'Idieresis': 600 213 | 'eacute': 600 214 | 'abreve': 600 215 | 'uhungarumlaut': 600 216 | 'ecaron': 600 217 | 'Ydieresis': 600 218 | 'divide': 600 219 | 'Yacute': 600 220 | 'Acircumflex': 600 221 | 'aacute': 600 222 | 'Ucircumflex': 600 223 | 'yacute': 600 224 | 'scommaaccent': 600 225 | 'ecircumflex': 600 226 | 'Uring': 600 227 | 'Udieresis': 600 228 | 'aogonek': 600 229 | 'Uacute': 600 230 | 'uogonek': 600 231 | 'Edieresis': 600 232 | 'Dcroat': 600 233 | 'commaaccent': 600 234 | 'copyright': 600 235 | 'Emacron': 600 236 | 'ccaron': 600 237 | 'aring': 600 238 | 'Ncommaaccent': 600 239 | 'lacute': 600 240 | 'agrave': 600 241 | 'Tcommaaccent': 600 242 | 'Cacute': 600 243 | 'atilde': 600 244 | 'Edotaccent': 600 245 | 'scaron': 600 246 | 'scedilla': 600 247 | 'iacute': 600 248 | 'lozenge': 600 249 | 'Rcaron': 600 250 | 'Gcommaaccent': 600 251 | 'ucircumflex': 600 252 | 'acircumflex': 600 253 | 'Amacron': 600 254 | 'rcaron': 600 255 | 'ccedilla': 600 256 | 'Zdotaccent': 600 257 | 'Thorn': 600 258 | 'Omacron': 600 259 | 'Racute': 600 260 | 'Sacute': 600 261 | 'dcaron': 600 262 | 'Umacron': 600 263 | 'uring': 600 264 | 'threesuperior': 600 265 | 'Ograve': 600 266 | 'Agrave': 600 267 | 'Abreve': 600 268 | 'multiply': 600 269 | 'uacute': 600 270 | 'Tcaron': 600 271 | 'partialdiff': 600 272 | 'ydieresis': 600 273 | 'Nacute': 600 274 | 'icircumflex': 600 275 | 'Ecircumflex': 600 276 | 'adieresis': 600 277 | 'edieresis': 600 278 | 'cacute': 600 279 | 'nacute': 600 280 | 'umacron': 600 281 | 'Ncaron': 600 282 | 'Iacute': 600 283 | 'plusminus': 600 284 | 'brokenbar': 600 285 | 'registered': 600 286 | 'Gbreve': 600 287 | 'Idotaccent': 600 288 | 'summation': 600 289 | 'Egrave': 600 290 | 'racute': 600 291 | 'omacron': 600 292 | 'Zacute': 600 293 | 'Zcaron': 600 294 | 'greaterequal': 600 295 | 'Eth': 600 296 | 'Ccedilla': 600 297 | 'lcommaaccent': 600 298 | 'tcaron': 600 299 | 'eogonek': 600 300 | 'Uogonek': 600 301 | 'Aacute': 600 302 | 'Adieresis': 600 303 | 'egrave': 600 304 | 'zacute': 600 305 | 'iogonek': 600 306 | 'Oacute': 600 307 | 'oacute': 600 308 | 'amacron': 600 309 | 'sacute': 600 310 | 'idieresis': 600 311 | 'Ocircumflex': 600 312 | 'Ugrave': 600 313 | 'Delta': 600 314 | 'thorn': 600 315 | 'twosuperior': 600 316 | 'Odieresis': 600 317 | 'mu': 600 318 | 'igrave': 600 319 | 'ohungarumlaut': 600 320 | 'Eogonek': 600 321 | 'dcroat': 600 322 | 'threequarters': 600 323 | 'Scedilla': 600 324 | 'lcaron': 600 325 | 'Kcommaaccent': 600 326 | 'Lacute': 600 327 | 'trademark': 600 328 | 'edotaccent': 600 329 | 'Igrave': 600 330 | 'Imacron': 600 331 | 'Lcaron': 600 332 | 'onehalf': 600 333 | 'lessequal': 600 334 | 'ocircumflex': 600 335 | 'ntilde': 600 336 | 'Uhungarumlaut': 600 337 | 'Eacute': 600 338 | 'emacron': 600 339 | 'gbreve': 600 340 | 'onequarter': 600 341 | 'Scaron': 600 342 | 'Scommaaccent': 600 343 | 'Ohungarumlaut': 600 344 | 'degree': 600 345 | 'ograve': 600 346 | 'Ccaron': 600 347 | 'ugrave': 600 348 | 'radical': 600 349 | 'Dcaron': 600 350 | 'rcommaaccent': 600 351 | 'Ntilde': 600 352 | 'otilde': 600 353 | 'Rcommaaccent': 600 354 | 'Lcommaaccent': 600 355 | 'Atilde': 600 356 | 'Aogonek': 600 357 | 'Aring': 600 358 | 'Otilde': 600 359 | 'zdotaccent': 600 360 | 'Ecaron': 600 361 | 'Iogonek': 600 362 | 'kcommaaccent': 600 363 | 'minus': 600 364 | 'Icircumflex': 600 365 | 'ncaron': 600 366 | 'tcommaaccent': 600 367 | 'logicalnot': 600 368 | 'odieresis': 600 369 | 'udieresis': 600 370 | 'notequal': 600 371 | 'gcommaaccent': 600 372 | 'eth': 600 373 | 'zcaron': 600 374 | 'ncommaaccent': 600 375 | 'onesuperior': 600 376 | 'imacron': 600 377 | 'Euro': 600 378 | } 379 | 'Courier-BoldOblique': { 380 | 'space': 600 381 | 'exclam': 600 382 | 'quotedbl': 600 383 | 'numbersign': 600 384 | 'dollar': 600 385 | 'percent': 600 386 | 'ampersand': 600 387 | 'quoteright': 600 388 | 'parenleft': 600 389 | 'parenright': 600 390 | 'asterisk': 600 391 | 'plus': 600 392 | 'comma': 600 393 | 'hyphen': 600 394 | 'period': 600 395 | 'slash': 600 396 | 'zero': 600 397 | 'one': 600 398 | 'two': 600 399 | 'three': 600 400 | 'four': 600 401 | 'five': 600 402 | 'six': 600 403 | 'seven': 600 404 | 'eight': 600 405 | 'nine': 600 406 | 'colon': 600 407 | 'semicolon': 600 408 | 'less': 600 409 | 'equal': 600 410 | 'greater': 600 411 | 'question': 600 412 | 'at': 600 413 | 'A': 600 414 | 'B': 600 415 | 'C': 600 416 | 'D': 600 417 | 'E': 600 418 | 'F': 600 419 | 'G': 600 420 | 'H': 600 421 | 'I': 600 422 | 'J': 600 423 | 'K': 600 424 | 'L': 600 425 | 'M': 600 426 | 'N': 600 427 | 'O': 600 428 | 'P': 600 429 | 'Q': 600 430 | 'R': 600 431 | 'S': 600 432 | 'T': 600 433 | 'U': 600 434 | 'V': 600 435 | 'W': 600 436 | 'X': 600 437 | 'Y': 600 438 | 'Z': 600 439 | 'bracketleft': 600 440 | 'backslash': 600 441 | 'bracketright': 600 442 | 'asciicircum': 600 443 | 'underscore': 600 444 | 'quoteleft': 600 445 | 'a': 600 446 | 'b': 600 447 | 'c': 600 448 | 'd': 600 449 | 'e': 600 450 | 'f': 600 451 | 'g': 600 452 | 'h': 600 453 | 'i': 600 454 | 'j': 600 455 | 'k': 600 456 | 'l': 600 457 | 'm': 600 458 | 'n': 600 459 | 'o': 600 460 | 'p': 600 461 | 'q': 600 462 | 'r': 600 463 | 's': 600 464 | 't': 600 465 | 'u': 600 466 | 'v': 600 467 | 'w': 600 468 | 'x': 600 469 | 'y': 600 470 | 'z': 600 471 | 'braceleft': 600 472 | 'bar': 600 473 | 'braceright': 600 474 | 'asciitilde': 600 475 | 'exclamdown': 600 476 | 'cent': 600 477 | 'sterling': 600 478 | 'fraction': 600 479 | 'yen': 600 480 | 'florin': 600 481 | 'section': 600 482 | 'currency': 600 483 | 'quotesingle': 600 484 | 'quotedblleft': 600 485 | 'guillemotleft': 600 486 | 'guilsinglleft': 600 487 | 'guilsinglright': 600 488 | 'fi': 600 489 | 'fl': 600 490 | 'endash': 600 491 | 'dagger': 600 492 | 'daggerdbl': 600 493 | 'periodcentered': 600 494 | 'paragraph': 600 495 | 'bullet': 600 496 | 'quotesinglbase': 600 497 | 'quotedblbase': 600 498 | 'quotedblright': 600 499 | 'guillemotright': 600 500 | 'ellipsis': 600 501 | 'perthousand': 600 502 | 'questiondown': 600 503 | 'grave': 600 504 | 'acute': 600 505 | 'circumflex': 600 506 | 'tilde': 600 507 | 'macron': 600 508 | 'breve': 600 509 | 'dotaccent': 600 510 | 'dieresis': 600 511 | 'ring': 600 512 | 'cedilla': 600 513 | 'hungarumlaut': 600 514 | 'ogonek': 600 515 | 'caron': 600 516 | 'emdash': 600 517 | 'AE': 600 518 | 'ordfeminine': 600 519 | 'Lslash': 600 520 | 'Oslash': 600 521 | 'OE': 600 522 | 'ordmasculine': 600 523 | 'ae': 600 524 | 'dotlessi': 600 525 | 'lslash': 600 526 | 'oslash': 600 527 | 'oe': 600 528 | 'germandbls': 600 529 | 'Idieresis': 600 530 | 'eacute': 600 531 | 'abreve': 600 532 | 'uhungarumlaut': 600 533 | 'ecaron': 600 534 | 'Ydieresis': 600 535 | 'divide': 600 536 | 'Yacute': 600 537 | 'Acircumflex': 600 538 | 'aacute': 600 539 | 'Ucircumflex': 600 540 | 'yacute': 600 541 | 'scommaaccent': 600 542 | 'ecircumflex': 600 543 | 'Uring': 600 544 | 'Udieresis': 600 545 | 'aogonek': 600 546 | 'Uacute': 600 547 | 'uogonek': 600 548 | 'Edieresis': 600 549 | 'Dcroat': 600 550 | 'commaaccent': 600 551 | 'copyright': 600 552 | 'Emacron': 600 553 | 'ccaron': 600 554 | 'aring': 600 555 | 'Ncommaaccent': 600 556 | 'lacute': 600 557 | 'agrave': 600 558 | 'Tcommaaccent': 600 559 | 'Cacute': 600 560 | 'atilde': 600 561 | 'Edotaccent': 600 562 | 'scaron': 600 563 | 'scedilla': 600 564 | 'iacute': 600 565 | 'lozenge': 600 566 | 'Rcaron': 600 567 | 'Gcommaaccent': 600 568 | 'ucircumflex': 600 569 | 'acircumflex': 600 570 | 'Amacron': 600 571 | 'rcaron': 600 572 | 'ccedilla': 600 573 | 'Zdotaccent': 600 574 | 'Thorn': 600 575 | 'Omacron': 600 576 | 'Racute': 600 577 | 'Sacute': 600 578 | 'dcaron': 600 579 | 'Umacron': 600 580 | 'uring': 600 581 | 'threesuperior': 600 582 | 'Ograve': 600 583 | 'Agrave': 600 584 | 'Abreve': 600 585 | 'multiply': 600 586 | 'uacute': 600 587 | 'Tcaron': 600 588 | 'partialdiff': 600 589 | 'ydieresis': 600 590 | 'Nacute': 600 591 | 'icircumflex': 600 592 | 'Ecircumflex': 600 593 | 'adieresis': 600 594 | 'edieresis': 600 595 | 'cacute': 600 596 | 'nacute': 600 597 | 'umacron': 600 598 | 'Ncaron': 600 599 | 'Iacute': 600 600 | 'plusminus': 600 601 | 'brokenbar': 600 602 | 'registered': 600 603 | 'Gbreve': 600 604 | 'Idotaccent': 600 605 | 'summation': 600 606 | 'Egrave': 600 607 | 'racute': 600 608 | 'omacron': 600 609 | 'Zacute': 600 610 | 'Zcaron': 600 611 | 'greaterequal': 600 612 | 'Eth': 600 613 | 'Ccedilla': 600 614 | 'lcommaaccent': 600 615 | 'tcaron': 600 616 | 'eogonek': 600 617 | 'Uogonek': 600 618 | 'Aacute': 600 619 | 'Adieresis': 600 620 | 'egrave': 600 621 | 'zacute': 600 622 | 'iogonek': 600 623 | 'Oacute': 600 624 | 'oacute': 600 625 | 'amacron': 600 626 | 'sacute': 600 627 | 'idieresis': 600 628 | 'Ocircumflex': 600 629 | 'Ugrave': 600 630 | 'Delta': 600 631 | 'thorn': 600 632 | 'twosuperior': 600 633 | 'Odieresis': 600 634 | 'mu': 600 635 | 'igrave': 600 636 | 'ohungarumlaut': 600 637 | 'Eogonek': 600 638 | 'dcroat': 600 639 | 'threequarters': 600 640 | 'Scedilla': 600 641 | 'lcaron': 600 642 | 'Kcommaaccent': 600 643 | 'Lacute': 600 644 | 'trademark': 600 645 | 'edotaccent': 600 646 | 'Igrave': 600 647 | 'Imacron': 600 648 | 'Lcaron': 600 649 | 'onehalf': 600 650 | 'lessequal': 600 651 | 'ocircumflex': 600 652 | 'ntilde': 600 653 | 'Uhungarumlaut': 600 654 | 'Eacute': 600 655 | 'emacron': 600 656 | 'gbreve': 600 657 | 'onequarter': 600 658 | 'Scaron': 600 659 | 'Scommaaccent': 600 660 | 'Ohungarumlaut': 600 661 | 'degree': 600 662 | 'ograve': 600 663 | 'Ccaron': 600 664 | 'ugrave': 600 665 | 'radical': 600 666 | 'Dcaron': 600 667 | 'rcommaaccent': 600 668 | 'Ntilde': 600 669 | 'otilde': 600 670 | 'Rcommaaccent': 600 671 | 'Lcommaaccent': 600 672 | 'Atilde': 600 673 | 'Aogonek': 600 674 | 'Aring': 600 675 | 'Otilde': 600 676 | 'zdotaccent': 600 677 | 'Ecaron': 600 678 | 'Iogonek': 600 679 | 'kcommaaccent': 600 680 | 'minus': 600 681 | 'Icircumflex': 600 682 | 'ncaron': 600 683 | 'tcommaaccent': 600 684 | 'logicalnot': 600 685 | 'odieresis': 600 686 | 'udieresis': 600 687 | 'notequal': 600 688 | 'gcommaaccent': 600 689 | 'eth': 600 690 | 'zcaron': 600 691 | 'ncommaaccent': 600 692 | 'onesuperior': 600 693 | 'imacron': 600 694 | 'Euro': 600 695 | } 696 | 'Courier-Oblique': { 697 | 'space': 600 698 | 'exclam': 600 699 | 'quotedbl': 600 700 | 'numbersign': 600 701 | 'dollar': 600 702 | 'percent': 600 703 | 'ampersand': 600 704 | 'quoteright': 600 705 | 'parenleft': 600 706 | 'parenright': 600 707 | 'asterisk': 600 708 | 'plus': 600 709 | 'comma': 600 710 | 'hyphen': 600 711 | 'period': 600 712 | 'slash': 600 713 | 'zero': 600 714 | 'one': 600 715 | 'two': 600 716 | 'three': 600 717 | 'four': 600 718 | 'five': 600 719 | 'six': 600 720 | 'seven': 600 721 | 'eight': 600 722 | 'nine': 600 723 | 'colon': 600 724 | 'semicolon': 600 725 | 'less': 600 726 | 'equal': 600 727 | 'greater': 600 728 | 'question': 600 729 | 'at': 600 730 | 'A': 600 731 | 'B': 600 732 | 'C': 600 733 | 'D': 600 734 | 'E': 600 735 | 'F': 600 736 | 'G': 600 737 | 'H': 600 738 | 'I': 600 739 | 'J': 600 740 | 'K': 600 741 | 'L': 600 742 | 'M': 600 743 | 'N': 600 744 | 'O': 600 745 | 'P': 600 746 | 'Q': 600 747 | 'R': 600 748 | 'S': 600 749 | 'T': 600 750 | 'U': 600 751 | 'V': 600 752 | 'W': 600 753 | 'X': 600 754 | 'Y': 600 755 | 'Z': 600 756 | 'bracketleft': 600 757 | 'backslash': 600 758 | 'bracketright': 600 759 | 'asciicircum': 600 760 | 'underscore': 600 761 | 'quoteleft': 600 762 | 'a': 600 763 | 'b': 600 764 | 'c': 600 765 | 'd': 600 766 | 'e': 600 767 | 'f': 600 768 | 'g': 600 769 | 'h': 600 770 | 'i': 600 771 | 'j': 600 772 | 'k': 600 773 | 'l': 600 774 | 'm': 600 775 | 'n': 600 776 | 'o': 600 777 | 'p': 600 778 | 'q': 600 779 | 'r': 600 780 | 's': 600 781 | 't': 600 782 | 'u': 600 783 | 'v': 600 784 | 'w': 600 785 | 'x': 600 786 | 'y': 600 787 | 'z': 600 788 | 'braceleft': 600 789 | 'bar': 600 790 | 'braceright': 600 791 | 'asciitilde': 600 792 | 'exclamdown': 600 793 | 'cent': 600 794 | 'sterling': 600 795 | 'fraction': 600 796 | 'yen': 600 797 | 'florin': 600 798 | 'section': 600 799 | 'currency': 600 800 | 'quotesingle': 600 801 | 'quotedblleft': 600 802 | 'guillemotleft': 600 803 | 'guilsinglleft': 600 804 | 'guilsinglright': 600 805 | 'fi': 600 806 | 'fl': 600 807 | 'endash': 600 808 | 'dagger': 600 809 | 'daggerdbl': 600 810 | 'periodcentered': 600 811 | 'paragraph': 600 812 | 'bullet': 600 813 | 'quotesinglbase': 600 814 | 'quotedblbase': 600 815 | 'quotedblright': 600 816 | 'guillemotright': 600 817 | 'ellipsis': 600 818 | 'perthousand': 600 819 | 'questiondown': 600 820 | 'grave': 600 821 | 'acute': 600 822 | 'circumflex': 600 823 | 'tilde': 600 824 | 'macron': 600 825 | 'breve': 600 826 | 'dotaccent': 600 827 | 'dieresis': 600 828 | 'ring': 600 829 | 'cedilla': 600 830 | 'hungarumlaut': 600 831 | 'ogonek': 600 832 | 'caron': 600 833 | 'emdash': 600 834 | 'AE': 600 835 | 'ordfeminine': 600 836 | 'Lslash': 600 837 | 'Oslash': 600 838 | 'OE': 600 839 | 'ordmasculine': 600 840 | 'ae': 600 841 | 'dotlessi': 600 842 | 'lslash': 600 843 | 'oslash': 600 844 | 'oe': 600 845 | 'germandbls': 600 846 | 'Idieresis': 600 847 | 'eacute': 600 848 | 'abreve': 600 849 | 'uhungarumlaut': 600 850 | 'ecaron': 600 851 | 'Ydieresis': 600 852 | 'divide': 600 853 | 'Yacute': 600 854 | 'Acircumflex': 600 855 | 'aacute': 600 856 | 'Ucircumflex': 600 857 | 'yacute': 600 858 | 'scommaaccent': 600 859 | 'ecircumflex': 600 860 | 'Uring': 600 861 | 'Udieresis': 600 862 | 'aogonek': 600 863 | 'Uacute': 600 864 | 'uogonek': 600 865 | 'Edieresis': 600 866 | 'Dcroat': 600 867 | 'commaaccent': 600 868 | 'copyright': 600 869 | 'Emacron': 600 870 | 'ccaron': 600 871 | 'aring': 600 872 | 'Ncommaaccent': 600 873 | 'lacute': 600 874 | 'agrave': 600 875 | 'Tcommaaccent': 600 876 | 'Cacute': 600 877 | 'atilde': 600 878 | 'Edotaccent': 600 879 | 'scaron': 600 880 | 'scedilla': 600 881 | 'iacute': 600 882 | 'lozenge': 600 883 | 'Rcaron': 600 884 | 'Gcommaaccent': 600 885 | 'ucircumflex': 600 886 | 'acircumflex': 600 887 | 'Amacron': 600 888 | 'rcaron': 600 889 | 'ccedilla': 600 890 | 'Zdotaccent': 600 891 | 'Thorn': 600 892 | 'Omacron': 600 893 | 'Racute': 600 894 | 'Sacute': 600 895 | 'dcaron': 600 896 | 'Umacron': 600 897 | 'uring': 600 898 | 'threesuperior': 600 899 | 'Ograve': 600 900 | 'Agrave': 600 901 | 'Abreve': 600 902 | 'multiply': 600 903 | 'uacute': 600 904 | 'Tcaron': 600 905 | 'partialdiff': 600 906 | 'ydieresis': 600 907 | 'Nacute': 600 908 | 'icircumflex': 600 909 | 'Ecircumflex': 600 910 | 'adieresis': 600 911 | 'edieresis': 600 912 | 'cacute': 600 913 | 'nacute': 600 914 | 'umacron': 600 915 | 'Ncaron': 600 916 | 'Iacute': 600 917 | 'plusminus': 600 918 | 'brokenbar': 600 919 | 'registered': 600 920 | 'Gbreve': 600 921 | 'Idotaccent': 600 922 | 'summation': 600 923 | 'Egrave': 600 924 | 'racute': 600 925 | 'omacron': 600 926 | 'Zacute': 600 927 | 'Zcaron': 600 928 | 'greaterequal': 600 929 | 'Eth': 600 930 | 'Ccedilla': 600 931 | 'lcommaaccent': 600 932 | 'tcaron': 600 933 | 'eogonek': 600 934 | 'Uogonek': 600 935 | 'Aacute': 600 936 | 'Adieresis': 600 937 | 'egrave': 600 938 | 'zacute': 600 939 | 'iogonek': 600 940 | 'Oacute': 600 941 | 'oacute': 600 942 | 'amacron': 600 943 | 'sacute': 600 944 | 'idieresis': 600 945 | 'Ocircumflex': 600 946 | 'Ugrave': 600 947 | 'Delta': 600 948 | 'thorn': 600 949 | 'twosuperior': 600 950 | 'Odieresis': 600 951 | 'mu': 600 952 | 'igrave': 600 953 | 'ohungarumlaut': 600 954 | 'Eogonek': 600 955 | 'dcroat': 600 956 | 'threequarters': 600 957 | 'Scedilla': 600 958 | 'lcaron': 600 959 | 'Kcommaaccent': 600 960 | 'Lacute': 600 961 | 'trademark': 600 962 | 'edotaccent': 600 963 | 'Igrave': 600 964 | 'Imacron': 600 965 | 'Lcaron': 600 966 | 'onehalf': 600 967 | 'lessequal': 600 968 | 'ocircumflex': 600 969 | 'ntilde': 600 970 | 'Uhungarumlaut': 600 971 | 'Eacute': 600 972 | 'emacron': 600 973 | 'gbreve': 600 974 | 'onequarter': 600 975 | 'Scaron': 600 976 | 'Scommaaccent': 600 977 | 'Ohungarumlaut': 600 978 | 'degree': 600 979 | 'ograve': 600 980 | 'Ccaron': 600 981 | 'ugrave': 600 982 | 'radical': 600 983 | 'Dcaron': 600 984 | 'rcommaaccent': 600 985 | 'Ntilde': 600 986 | 'otilde': 600 987 | 'Rcommaaccent': 600 988 | 'Lcommaaccent': 600 989 | 'Atilde': 600 990 | 'Aogonek': 600 991 | 'Aring': 600 992 | 'Otilde': 600 993 | 'zdotaccent': 600 994 | 'Ecaron': 600 995 | 'Iogonek': 600 996 | 'kcommaaccent': 600 997 | 'minus': 600 998 | 'Icircumflex': 600 999 | 'ncaron': 600 1000 | 'tcommaaccent': 600 1001 | 'logicalnot': 600 1002 | 'odieresis': 600 1003 | 'udieresis': 600 1004 | 'notequal': 600 1005 | 'gcommaaccent': 600 1006 | 'eth': 600 1007 | 'zcaron': 600 1008 | 'ncommaaccent': 600 1009 | 'onesuperior': 600 1010 | 'imacron': 600 1011 | 'Euro': 600 1012 | } 1013 | 'Courier': { 1014 | 'space': 600 1015 | 'exclam': 600 1016 | 'quotedbl': 600 1017 | 'numbersign': 600 1018 | 'dollar': 600 1019 | 'percent': 600 1020 | 'ampersand': 600 1021 | 'quoteright': 600 1022 | 'parenleft': 600 1023 | 'parenright': 600 1024 | 'asterisk': 600 1025 | 'plus': 600 1026 | 'comma': 600 1027 | 'hyphen': 600 1028 | 'period': 600 1029 | 'slash': 600 1030 | 'zero': 600 1031 | 'one': 600 1032 | 'two': 600 1033 | 'three': 600 1034 | 'four': 600 1035 | 'five': 600 1036 | 'six': 600 1037 | 'seven': 600 1038 | 'eight': 600 1039 | 'nine': 600 1040 | 'colon': 600 1041 | 'semicolon': 600 1042 | 'less': 600 1043 | 'equal': 600 1044 | 'greater': 600 1045 | 'question': 600 1046 | 'at': 600 1047 | 'A': 600 1048 | 'B': 600 1049 | 'C': 600 1050 | 'D': 600 1051 | 'E': 600 1052 | 'F': 600 1053 | 'G': 600 1054 | 'H': 600 1055 | 'I': 600 1056 | 'J': 600 1057 | 'K': 600 1058 | 'L': 600 1059 | 'M': 600 1060 | 'N': 600 1061 | 'O': 600 1062 | 'P': 600 1063 | 'Q': 600 1064 | 'R': 600 1065 | 'S': 600 1066 | 'T': 600 1067 | 'U': 600 1068 | 'V': 600 1069 | 'W': 600 1070 | 'X': 600 1071 | 'Y': 600 1072 | 'Z': 600 1073 | 'bracketleft': 600 1074 | 'backslash': 600 1075 | 'bracketright': 600 1076 | 'asciicircum': 600 1077 | 'underscore': 600 1078 | 'quoteleft': 600 1079 | 'a': 600 1080 | 'b': 600 1081 | 'c': 600 1082 | 'd': 600 1083 | 'e': 600 1084 | 'f': 600 1085 | 'g': 600 1086 | 'h': 600 1087 | 'i': 600 1088 | 'j': 600 1089 | 'k': 600 1090 | 'l': 600 1091 | 'm': 600 1092 | 'n': 600 1093 | 'o': 600 1094 | 'p': 600 1095 | 'q': 600 1096 | 'r': 600 1097 | 's': 600 1098 | 't': 600 1099 | 'u': 600 1100 | 'v': 600 1101 | 'w': 600 1102 | 'x': 600 1103 | 'y': 600 1104 | 'z': 600 1105 | 'braceleft': 600 1106 | 'bar': 600 1107 | 'braceright': 600 1108 | 'asciitilde': 600 1109 | 'exclamdown': 600 1110 | 'cent': 600 1111 | 'sterling': 600 1112 | 'fraction': 600 1113 | 'yen': 600 1114 | 'florin': 600 1115 | 'section': 600 1116 | 'currency': 600 1117 | 'quotesingle': 600 1118 | 'quotedblleft': 600 1119 | 'guillemotleft': 600 1120 | 'guilsinglleft': 600 1121 | 'guilsinglright': 600 1122 | 'fi': 600 1123 | 'fl': 600 1124 | 'endash': 600 1125 | 'dagger': 600 1126 | 'daggerdbl': 600 1127 | 'periodcentered': 600 1128 | 'paragraph': 600 1129 | 'bullet': 600 1130 | 'quotesinglbase': 600 1131 | 'quotedblbase': 600 1132 | 'quotedblright': 600 1133 | 'guillemotright': 600 1134 | 'ellipsis': 600 1135 | 'perthousand': 600 1136 | 'questiondown': 600 1137 | 'grave': 600 1138 | 'acute': 600 1139 | 'circumflex': 600 1140 | 'tilde': 600 1141 | 'macron': 600 1142 | 'breve': 600 1143 | 'dotaccent': 600 1144 | 'dieresis': 600 1145 | 'ring': 600 1146 | 'cedilla': 600 1147 | 'hungarumlaut': 600 1148 | 'ogonek': 600 1149 | 'caron': 600 1150 | 'emdash': 600 1151 | 'AE': 600 1152 | 'ordfeminine': 600 1153 | 'Lslash': 600 1154 | 'Oslash': 600 1155 | 'OE': 600 1156 | 'ordmasculine': 600 1157 | 'ae': 600 1158 | 'dotlessi': 600 1159 | 'lslash': 600 1160 | 'oslash': 600 1161 | 'oe': 600 1162 | 'germandbls': 600 1163 | 'Idieresis': 600 1164 | 'eacute': 600 1165 | 'abreve': 600 1166 | 'uhungarumlaut': 600 1167 | 'ecaron': 600 1168 | 'Ydieresis': 600 1169 | 'divide': 600 1170 | 'Yacute': 600 1171 | 'Acircumflex': 600 1172 | 'aacute': 600 1173 | 'Ucircumflex': 600 1174 | 'yacute': 600 1175 | 'scommaaccent': 600 1176 | 'ecircumflex': 600 1177 | 'Uring': 600 1178 | 'Udieresis': 600 1179 | 'aogonek': 600 1180 | 'Uacute': 600 1181 | 'uogonek': 600 1182 | 'Edieresis': 600 1183 | 'Dcroat': 600 1184 | 'commaaccent': 600 1185 | 'copyright': 600 1186 | 'Emacron': 600 1187 | 'ccaron': 600 1188 | 'aring': 600 1189 | 'Ncommaaccent': 600 1190 | 'lacute': 600 1191 | 'agrave': 600 1192 | 'Tcommaaccent': 600 1193 | 'Cacute': 600 1194 | 'atilde': 600 1195 | 'Edotaccent': 600 1196 | 'scaron': 600 1197 | 'scedilla': 600 1198 | 'iacute': 600 1199 | 'lozenge': 600 1200 | 'Rcaron': 600 1201 | 'Gcommaaccent': 600 1202 | 'ucircumflex': 600 1203 | 'acircumflex': 600 1204 | 'Amacron': 600 1205 | 'rcaron': 600 1206 | 'ccedilla': 600 1207 | 'Zdotaccent': 600 1208 | 'Thorn': 600 1209 | 'Omacron': 600 1210 | 'Racute': 600 1211 | 'Sacute': 600 1212 | 'dcaron': 600 1213 | 'Umacron': 600 1214 | 'uring': 600 1215 | 'threesuperior': 600 1216 | 'Ograve': 600 1217 | 'Agrave': 600 1218 | 'Abreve': 600 1219 | 'multiply': 600 1220 | 'uacute': 600 1221 | 'Tcaron': 600 1222 | 'partialdiff': 600 1223 | 'ydieresis': 600 1224 | 'Nacute': 600 1225 | 'icircumflex': 600 1226 | 'Ecircumflex': 600 1227 | 'adieresis': 600 1228 | 'edieresis': 600 1229 | 'cacute': 600 1230 | 'nacute': 600 1231 | 'umacron': 600 1232 | 'Ncaron': 600 1233 | 'Iacute': 600 1234 | 'plusminus': 600 1235 | 'brokenbar': 600 1236 | 'registered': 600 1237 | 'Gbreve': 600 1238 | 'Idotaccent': 600 1239 | 'summation': 600 1240 | 'Egrave': 600 1241 | 'racute': 600 1242 | 'omacron': 600 1243 | 'Zacute': 600 1244 | 'Zcaron': 600 1245 | 'greaterequal': 600 1246 | 'Eth': 600 1247 | 'Ccedilla': 600 1248 | 'lcommaaccent': 600 1249 | 'tcaron': 600 1250 | 'eogonek': 600 1251 | 'Uogonek': 600 1252 | 'Aacute': 600 1253 | 'Adieresis': 600 1254 | 'egrave': 600 1255 | 'zacute': 600 1256 | 'iogonek': 600 1257 | 'Oacute': 600 1258 | 'oacute': 600 1259 | 'amacron': 600 1260 | 'sacute': 600 1261 | 'idieresis': 600 1262 | 'Ocircumflex': 600 1263 | 'Ugrave': 600 1264 | 'Delta': 600 1265 | 'thorn': 600 1266 | 'twosuperior': 600 1267 | 'Odieresis': 600 1268 | 'mu': 600 1269 | 'igrave': 600 1270 | 'ohungarumlaut': 600 1271 | 'Eogonek': 600 1272 | 'dcroat': 600 1273 | 'threequarters': 600 1274 | 'Scedilla': 600 1275 | 'lcaron': 600 1276 | 'Kcommaaccent': 600 1277 | 'Lacute': 600 1278 | 'trademark': 600 1279 | 'edotaccent': 600 1280 | 'Igrave': 600 1281 | 'Imacron': 600 1282 | 'Lcaron': 600 1283 | 'onehalf': 600 1284 | 'lessequal': 600 1285 | 'ocircumflex': 600 1286 | 'ntilde': 600 1287 | 'Uhungarumlaut': 600 1288 | 'Eacute': 600 1289 | 'emacron': 600 1290 | 'gbreve': 600 1291 | 'onequarter': 600 1292 | 'Scaron': 600 1293 | 'Scommaaccent': 600 1294 | 'Ohungarumlaut': 600 1295 | 'degree': 600 1296 | 'ograve': 600 1297 | 'Ccaron': 600 1298 | 'ugrave': 600 1299 | 'radical': 600 1300 | 'Dcaron': 600 1301 | 'rcommaaccent': 600 1302 | 'Ntilde': 600 1303 | 'otilde': 600 1304 | 'Rcommaaccent': 600 1305 | 'Lcommaaccent': 600 1306 | 'Atilde': 600 1307 | 'Aogonek': 600 1308 | 'Aring': 600 1309 | 'Otilde': 600 1310 | 'zdotaccent': 600 1311 | 'Ecaron': 600 1312 | 'Iogonek': 600 1313 | 'kcommaaccent': 600 1314 | 'minus': 600 1315 | 'Icircumflex': 600 1316 | 'ncaron': 600 1317 | 'tcommaaccent': 600 1318 | 'logicalnot': 600 1319 | 'odieresis': 600 1320 | 'udieresis': 600 1321 | 'notequal': 600 1322 | 'gcommaaccent': 600 1323 | 'eth': 600 1324 | 'zcaron': 600 1325 | 'ncommaaccent': 600 1326 | 'onesuperior': 600 1327 | 'imacron': 600 1328 | 'Euro': 600 1329 | } 1330 | 'Helvetica-Bold': { 1331 | 'space': 278 1332 | 'exclam': 333 1333 | 'quotedbl': 474 1334 | 'numbersign': 556 1335 | 'dollar': 556 1336 | 'percent': 889 1337 | 'ampersand': 722 1338 | 'quoteright': 278 1339 | 'parenleft': 333 1340 | 'parenright': 333 1341 | 'asterisk': 389 1342 | 'plus': 584 1343 | 'comma': 278 1344 | 'hyphen': 333 1345 | 'period': 278 1346 | 'slash': 278 1347 | 'zero': 556 1348 | 'one': 556 1349 | 'two': 556 1350 | 'three': 556 1351 | 'four': 556 1352 | 'five': 556 1353 | 'six': 556 1354 | 'seven': 556 1355 | 'eight': 556 1356 | 'nine': 556 1357 | 'colon': 333 1358 | 'semicolon': 333 1359 | 'less': 584 1360 | 'equal': 584 1361 | 'greater': 584 1362 | 'question': 611 1363 | 'at': 975 1364 | 'A': 722 1365 | 'B': 722 1366 | 'C': 722 1367 | 'D': 722 1368 | 'E': 667 1369 | 'F': 611 1370 | 'G': 778 1371 | 'H': 722 1372 | 'I': 278 1373 | 'J': 556 1374 | 'K': 722 1375 | 'L': 611 1376 | 'M': 833 1377 | 'N': 722 1378 | 'O': 778 1379 | 'P': 667 1380 | 'Q': 778 1381 | 'R': 722 1382 | 'S': 667 1383 | 'T': 611 1384 | 'U': 722 1385 | 'V': 667 1386 | 'W': 944 1387 | 'X': 667 1388 | 'Y': 667 1389 | 'Z': 611 1390 | 'bracketleft': 333 1391 | 'backslash': 278 1392 | 'bracketright': 333 1393 | 'asciicircum': 584 1394 | 'underscore': 556 1395 | 'quoteleft': 278 1396 | 'a': 556 1397 | 'b': 611 1398 | 'c': 556 1399 | 'd': 611 1400 | 'e': 556 1401 | 'f': 333 1402 | 'g': 611 1403 | 'h': 611 1404 | 'i': 278 1405 | 'j': 278 1406 | 'k': 556 1407 | 'l': 278 1408 | 'm': 889 1409 | 'n': 611 1410 | 'o': 611 1411 | 'p': 611 1412 | 'q': 611 1413 | 'r': 389 1414 | 's': 556 1415 | 't': 333 1416 | 'u': 611 1417 | 'v': 556 1418 | 'w': 778 1419 | 'x': 556 1420 | 'y': 556 1421 | 'z': 500 1422 | 'braceleft': 389 1423 | 'bar': 280 1424 | 'braceright': 389 1425 | 'asciitilde': 584 1426 | 'exclamdown': 333 1427 | 'cent': 556 1428 | 'sterling': 556 1429 | 'fraction': 167 1430 | 'yen': 556 1431 | 'florin': 556 1432 | 'section': 556 1433 | 'currency': 556 1434 | 'quotesingle': 238 1435 | 'quotedblleft': 500 1436 | 'guillemotleft': 556 1437 | 'guilsinglleft': 333 1438 | 'guilsinglright': 333 1439 | 'fi': 611 1440 | 'fl': 611 1441 | 'endash': 556 1442 | 'dagger': 556 1443 | 'daggerdbl': 556 1444 | 'periodcentered': 278 1445 | 'paragraph': 556 1446 | 'bullet': 350 1447 | 'quotesinglbase': 278 1448 | 'quotedblbase': 500 1449 | 'quotedblright': 500 1450 | 'guillemotright': 556 1451 | 'ellipsis': 1000 1452 | 'perthousand': 1000 1453 | 'questiondown': 611 1454 | 'grave': 333 1455 | 'acute': 333 1456 | 'circumflex': 333 1457 | 'tilde': 333 1458 | 'macron': 333 1459 | 'breve': 333 1460 | 'dotaccent': 333 1461 | 'dieresis': 333 1462 | 'ring': 333 1463 | 'cedilla': 333 1464 | 'hungarumlaut': 333 1465 | 'ogonek': 333 1466 | 'caron': 333 1467 | 'emdash': 1000 1468 | 'AE': 1000 1469 | 'ordfeminine': 370 1470 | 'Lslash': 611 1471 | 'Oslash': 778 1472 | 'OE': 1000 1473 | 'ordmasculine': 365 1474 | 'ae': 889 1475 | 'dotlessi': 278 1476 | 'lslash': 278 1477 | 'oslash': 611 1478 | 'oe': 944 1479 | 'germandbls': 611 1480 | 'Idieresis': 278 1481 | 'eacute': 556 1482 | 'abreve': 556 1483 | 'uhungarumlaut': 611 1484 | 'ecaron': 556 1485 | 'Ydieresis': 667 1486 | 'divide': 584 1487 | 'Yacute': 667 1488 | 'Acircumflex': 722 1489 | 'aacute': 556 1490 | 'Ucircumflex': 722 1491 | 'yacute': 556 1492 | 'scommaaccent': 556 1493 | 'ecircumflex': 556 1494 | 'Uring': 722 1495 | 'Udieresis': 722 1496 | 'aogonek': 556 1497 | 'Uacute': 722 1498 | 'uogonek': 611 1499 | 'Edieresis': 667 1500 | 'Dcroat': 722 1501 | 'commaaccent': 250 1502 | 'copyright': 737 1503 | 'Emacron': 667 1504 | 'ccaron': 556 1505 | 'aring': 556 1506 | 'Ncommaaccent': 722 1507 | 'lacute': 278 1508 | 'agrave': 556 1509 | 'Tcommaaccent': 611 1510 | 'Cacute': 722 1511 | 'atilde': 556 1512 | 'Edotaccent': 667 1513 | 'scaron': 556 1514 | 'scedilla': 556 1515 | 'iacute': 278 1516 | 'lozenge': 494 1517 | 'Rcaron': 722 1518 | 'Gcommaaccent': 778 1519 | 'ucircumflex': 611 1520 | 'acircumflex': 556 1521 | 'Amacron': 722 1522 | 'rcaron': 389 1523 | 'ccedilla': 556 1524 | 'Zdotaccent': 611 1525 | 'Thorn': 667 1526 | 'Omacron': 778 1527 | 'Racute': 722 1528 | 'Sacute': 667 1529 | 'dcaron': 743 1530 | 'Umacron': 722 1531 | 'uring': 611 1532 | 'threesuperior': 333 1533 | 'Ograve': 778 1534 | 'Agrave': 722 1535 | 'Abreve': 722 1536 | 'multiply': 584 1537 | 'uacute': 611 1538 | 'Tcaron': 611 1539 | 'partialdiff': 494 1540 | 'ydieresis': 556 1541 | 'Nacute': 722 1542 | 'icircumflex': 278 1543 | 'Ecircumflex': 667 1544 | 'adieresis': 556 1545 | 'edieresis': 556 1546 | 'cacute': 556 1547 | 'nacute': 611 1548 | 'umacron': 611 1549 | 'Ncaron': 722 1550 | 'Iacute': 278 1551 | 'plusminus': 584 1552 | 'brokenbar': 280 1553 | 'registered': 737 1554 | 'Gbreve': 778 1555 | 'Idotaccent': 278 1556 | 'summation': 600 1557 | 'Egrave': 667 1558 | 'racute': 389 1559 | 'omacron': 611 1560 | 'Zacute': 611 1561 | 'Zcaron': 611 1562 | 'greaterequal': 549 1563 | 'Eth': 722 1564 | 'Ccedilla': 722 1565 | 'lcommaaccent': 278 1566 | 'tcaron': 389 1567 | 'eogonek': 556 1568 | 'Uogonek': 722 1569 | 'Aacute': 722 1570 | 'Adieresis': 722 1571 | 'egrave': 556 1572 | 'zacute': 500 1573 | 'iogonek': 278 1574 | 'Oacute': 778 1575 | 'oacute': 611 1576 | 'amacron': 556 1577 | 'sacute': 556 1578 | 'idieresis': 278 1579 | 'Ocircumflex': 778 1580 | 'Ugrave': 722 1581 | 'Delta': 612 1582 | 'thorn': 611 1583 | 'twosuperior': 333 1584 | 'Odieresis': 778 1585 | 'mu': 611 1586 | 'igrave': 278 1587 | 'ohungarumlaut': 611 1588 | 'Eogonek': 667 1589 | 'dcroat': 611 1590 | 'threequarters': 834 1591 | 'Scedilla': 667 1592 | 'lcaron': 400 1593 | 'Kcommaaccent': 722 1594 | 'Lacute': 611 1595 | 'trademark': 1000 1596 | 'edotaccent': 556 1597 | 'Igrave': 278 1598 | 'Imacron': 278 1599 | 'Lcaron': 611 1600 | 'onehalf': 834 1601 | 'lessequal': 549 1602 | 'ocircumflex': 611 1603 | 'ntilde': 611 1604 | 'Uhungarumlaut': 722 1605 | 'Eacute': 667 1606 | 'emacron': 556 1607 | 'gbreve': 611 1608 | 'onequarter': 834 1609 | 'Scaron': 667 1610 | 'Scommaaccent': 667 1611 | 'Ohungarumlaut': 778 1612 | 'degree': 400 1613 | 'ograve': 611 1614 | 'Ccaron': 722 1615 | 'ugrave': 611 1616 | 'radical': 549 1617 | 'Dcaron': 722 1618 | 'rcommaaccent': 389 1619 | 'Ntilde': 722 1620 | 'otilde': 611 1621 | 'Rcommaaccent': 722 1622 | 'Lcommaaccent': 611 1623 | 'Atilde': 722 1624 | 'Aogonek': 722 1625 | 'Aring': 722 1626 | 'Otilde': 778 1627 | 'zdotaccent': 500 1628 | 'Ecaron': 667 1629 | 'Iogonek': 278 1630 | 'kcommaaccent': 556 1631 | 'minus': 584 1632 | 'Icircumflex': 278 1633 | 'ncaron': 611 1634 | 'tcommaaccent': 333 1635 | 'logicalnot': 584 1636 | 'odieresis': 611 1637 | 'udieresis': 611 1638 | 'notequal': 549 1639 | 'gcommaaccent': 611 1640 | 'eth': 611 1641 | 'zcaron': 500 1642 | 'ncommaaccent': 611 1643 | 'onesuperior': 333 1644 | 'imacron': 278 1645 | 'Euro': 556 1646 | } 1647 | 'Helvetica-BoldOblique': { 1648 | 'space': 278 1649 | 'exclam': 333 1650 | 'quotedbl': 474 1651 | 'numbersign': 556 1652 | 'dollar': 556 1653 | 'percent': 889 1654 | 'ampersand': 722 1655 | 'quoteright': 278 1656 | 'parenleft': 333 1657 | 'parenright': 333 1658 | 'asterisk': 389 1659 | 'plus': 584 1660 | 'comma': 278 1661 | 'hyphen': 333 1662 | 'period': 278 1663 | 'slash': 278 1664 | 'zero': 556 1665 | 'one': 556 1666 | 'two': 556 1667 | 'three': 556 1668 | 'four': 556 1669 | 'five': 556 1670 | 'six': 556 1671 | 'seven': 556 1672 | 'eight': 556 1673 | 'nine': 556 1674 | 'colon': 333 1675 | 'semicolon': 333 1676 | 'less': 584 1677 | 'equal': 584 1678 | 'greater': 584 1679 | 'question': 611 1680 | 'at': 975 1681 | 'A': 722 1682 | 'B': 722 1683 | 'C': 722 1684 | 'D': 722 1685 | 'E': 667 1686 | 'F': 611 1687 | 'G': 778 1688 | 'H': 722 1689 | 'I': 278 1690 | 'J': 556 1691 | 'K': 722 1692 | 'L': 611 1693 | 'M': 833 1694 | 'N': 722 1695 | 'O': 778 1696 | 'P': 667 1697 | 'Q': 778 1698 | 'R': 722 1699 | 'S': 667 1700 | 'T': 611 1701 | 'U': 722 1702 | 'V': 667 1703 | 'W': 944 1704 | 'X': 667 1705 | 'Y': 667 1706 | 'Z': 611 1707 | 'bracketleft': 333 1708 | 'backslash': 278 1709 | 'bracketright': 333 1710 | 'asciicircum': 584 1711 | 'underscore': 556 1712 | 'quoteleft': 278 1713 | 'a': 556 1714 | 'b': 611 1715 | 'c': 556 1716 | 'd': 611 1717 | 'e': 556 1718 | 'f': 333 1719 | 'g': 611 1720 | 'h': 611 1721 | 'i': 278 1722 | 'j': 278 1723 | 'k': 556 1724 | 'l': 278 1725 | 'm': 889 1726 | 'n': 611 1727 | 'o': 611 1728 | 'p': 611 1729 | 'q': 611 1730 | 'r': 389 1731 | 's': 556 1732 | 't': 333 1733 | 'u': 611 1734 | 'v': 556 1735 | 'w': 778 1736 | 'x': 556 1737 | 'y': 556 1738 | 'z': 500 1739 | 'braceleft': 389 1740 | 'bar': 280 1741 | 'braceright': 389 1742 | 'asciitilde': 584 1743 | 'exclamdown': 333 1744 | 'cent': 556 1745 | 'sterling': 556 1746 | 'fraction': 167 1747 | 'yen': 556 1748 | 'florin': 556 1749 | 'section': 556 1750 | 'currency': 556 1751 | 'quotesingle': 238 1752 | 'quotedblleft': 500 1753 | 'guillemotleft': 556 1754 | 'guilsinglleft': 333 1755 | 'guilsinglright': 333 1756 | 'fi': 611 1757 | 'fl': 611 1758 | 'endash': 556 1759 | 'dagger': 556 1760 | 'daggerdbl': 556 1761 | 'periodcentered': 278 1762 | 'paragraph': 556 1763 | 'bullet': 350 1764 | 'quotesinglbase': 278 1765 | 'quotedblbase': 500 1766 | 'quotedblright': 500 1767 | 'guillemotright': 556 1768 | 'ellipsis': 1000 1769 | 'perthousand': 1000 1770 | 'questiondown': 611 1771 | 'grave': 333 1772 | 'acute': 333 1773 | 'circumflex': 333 1774 | 'tilde': 333 1775 | 'macron': 333 1776 | 'breve': 333 1777 | 'dotaccent': 333 1778 | 'dieresis': 333 1779 | 'ring': 333 1780 | 'cedilla': 333 1781 | 'hungarumlaut': 333 1782 | 'ogonek': 333 1783 | 'caron': 333 1784 | 'emdash': 1000 1785 | 'AE': 1000 1786 | 'ordfeminine': 370 1787 | 'Lslash': 611 1788 | 'Oslash': 778 1789 | 'OE': 1000 1790 | 'ordmasculine': 365 1791 | 'ae': 889 1792 | 'dotlessi': 278 1793 | 'lslash': 278 1794 | 'oslash': 611 1795 | 'oe': 944 1796 | 'germandbls': 611 1797 | 'Idieresis': 278 1798 | 'eacute': 556 1799 | 'abreve': 556 1800 | 'uhungarumlaut': 611 1801 | 'ecaron': 556 1802 | 'Ydieresis': 667 1803 | 'divide': 584 1804 | 'Yacute': 667 1805 | 'Acircumflex': 722 1806 | 'aacute': 556 1807 | 'Ucircumflex': 722 1808 | 'yacute': 556 1809 | 'scommaaccent': 556 1810 | 'ecircumflex': 556 1811 | 'Uring': 722 1812 | 'Udieresis': 722 1813 | 'aogonek': 556 1814 | 'Uacute': 722 1815 | 'uogonek': 611 1816 | 'Edieresis': 667 1817 | 'Dcroat': 722 1818 | 'commaaccent': 250 1819 | 'copyright': 737 1820 | 'Emacron': 667 1821 | 'ccaron': 556 1822 | 'aring': 556 1823 | 'Ncommaaccent': 722 1824 | 'lacute': 278 1825 | 'agrave': 556 1826 | 'Tcommaaccent': 611 1827 | 'Cacute': 722 1828 | 'atilde': 556 1829 | 'Edotaccent': 667 1830 | 'scaron': 556 1831 | 'scedilla': 556 1832 | 'iacute': 278 1833 | 'lozenge': 494 1834 | 'Rcaron': 722 1835 | 'Gcommaaccent': 778 1836 | 'ucircumflex': 611 1837 | 'acircumflex': 556 1838 | 'Amacron': 722 1839 | 'rcaron': 389 1840 | 'ccedilla': 556 1841 | 'Zdotaccent': 611 1842 | 'Thorn': 667 1843 | 'Omacron': 778 1844 | 'Racute': 722 1845 | 'Sacute': 667 1846 | 'dcaron': 743 1847 | 'Umacron': 722 1848 | 'uring': 611 1849 | 'threesuperior': 333 1850 | 'Ograve': 778 1851 | 'Agrave': 722 1852 | 'Abreve': 722 1853 | 'multiply': 584 1854 | 'uacute': 611 1855 | 'Tcaron': 611 1856 | 'partialdiff': 494 1857 | 'ydieresis': 556 1858 | 'Nacute': 722 1859 | 'icircumflex': 278 1860 | 'Ecircumflex': 667 1861 | 'adieresis': 556 1862 | 'edieresis': 556 1863 | 'cacute': 556 1864 | 'nacute': 611 1865 | 'umacron': 611 1866 | 'Ncaron': 722 1867 | 'Iacute': 278 1868 | 'plusminus': 584 1869 | 'brokenbar': 280 1870 | 'registered': 737 1871 | 'Gbreve': 778 1872 | 'Idotaccent': 278 1873 | 'summation': 600 1874 | 'Egrave': 667 1875 | 'racute': 389 1876 | 'omacron': 611 1877 | 'Zacute': 611 1878 | 'Zcaron': 611 1879 | 'greaterequal': 549 1880 | 'Eth': 722 1881 | 'Ccedilla': 722 1882 | 'lcommaaccent': 278 1883 | 'tcaron': 389 1884 | 'eogonek': 556 1885 | 'Uogonek': 722 1886 | 'Aacute': 722 1887 | 'Adieresis': 722 1888 | 'egrave': 556 1889 | 'zacute': 500 1890 | 'iogonek': 278 1891 | 'Oacute': 778 1892 | 'oacute': 611 1893 | 'amacron': 556 1894 | 'sacute': 556 1895 | 'idieresis': 278 1896 | 'Ocircumflex': 778 1897 | 'Ugrave': 722 1898 | 'Delta': 612 1899 | 'thorn': 611 1900 | 'twosuperior': 333 1901 | 'Odieresis': 778 1902 | 'mu': 611 1903 | 'igrave': 278 1904 | 'ohungarumlaut': 611 1905 | 'Eogonek': 667 1906 | 'dcroat': 611 1907 | 'threequarters': 834 1908 | 'Scedilla': 667 1909 | 'lcaron': 400 1910 | 'Kcommaaccent': 722 1911 | 'Lacute': 611 1912 | 'trademark': 1000 1913 | 'edotaccent': 556 1914 | 'Igrave': 278 1915 | 'Imacron': 278 1916 | 'Lcaron': 611 1917 | 'onehalf': 834 1918 | 'lessequal': 549 1919 | 'ocircumflex': 611 1920 | 'ntilde': 611 1921 | 'Uhungarumlaut': 722 1922 | 'Eacute': 667 1923 | 'emacron': 556 1924 | 'gbreve': 611 1925 | 'onequarter': 834 1926 | 'Scaron': 667 1927 | 'Scommaaccent': 667 1928 | 'Ohungarumlaut': 778 1929 | 'degree': 400 1930 | 'ograve': 611 1931 | 'Ccaron': 722 1932 | 'ugrave': 611 1933 | 'radical': 549 1934 | 'Dcaron': 722 1935 | 'rcommaaccent': 389 1936 | 'Ntilde': 722 1937 | 'otilde': 611 1938 | 'Rcommaaccent': 722 1939 | 'Lcommaaccent': 611 1940 | 'Atilde': 722 1941 | 'Aogonek': 722 1942 | 'Aring': 722 1943 | 'Otilde': 778 1944 | 'zdotaccent': 500 1945 | 'Ecaron': 667 1946 | 'Iogonek': 278 1947 | 'kcommaaccent': 556 1948 | 'minus': 584 1949 | 'Icircumflex': 278 1950 | 'ncaron': 611 1951 | 'tcommaaccent': 333 1952 | 'logicalnot': 584 1953 | 'odieresis': 611 1954 | 'udieresis': 611 1955 | 'notequal': 549 1956 | 'gcommaaccent': 611 1957 | 'eth': 611 1958 | 'zcaron': 500 1959 | 'ncommaaccent': 611 1960 | 'onesuperior': 333 1961 | 'imacron': 278 1962 | 'Euro': 556 1963 | } 1964 | 'Helvetica-Oblique': { 1965 | 'space': 278 1966 | 'exclam': 278 1967 | 'quotedbl': 355 1968 | 'numbersign': 556 1969 | 'dollar': 556 1970 | 'percent': 889 1971 | 'ampersand': 667 1972 | 'quoteright': 222 1973 | 'parenleft': 333 1974 | 'parenright': 333 1975 | 'asterisk': 389 1976 | 'plus': 584 1977 | 'comma': 278 1978 | 'hyphen': 333 1979 | 'period': 278 1980 | 'slash': 278 1981 | 'zero': 556 1982 | 'one': 556 1983 | 'two': 556 1984 | 'three': 556 1985 | 'four': 556 1986 | 'five': 556 1987 | 'six': 556 1988 | 'seven': 556 1989 | 'eight': 556 1990 | 'nine': 556 1991 | 'colon': 278 1992 | 'semicolon': 278 1993 | 'less': 584 1994 | 'equal': 584 1995 | 'greater': 584 1996 | 'question': 556 1997 | 'at': 1015 1998 | 'A': 667 1999 | 'B': 667 2000 | 'C': 722 2001 | 'D': 722 2002 | 'E': 667 2003 | 'F': 611 2004 | 'G': 778 2005 | 'H': 722 2006 | 'I': 278 2007 | 'J': 500 2008 | 'K': 667 2009 | 'L': 556 2010 | 'M': 833 2011 | 'N': 722 2012 | 'O': 778 2013 | 'P': 667 2014 | 'Q': 778 2015 | 'R': 722 2016 | 'S': 667 2017 | 'T': 611 2018 | 'U': 722 2019 | 'V': 667 2020 | 'W': 944 2021 | 'X': 667 2022 | 'Y': 667 2023 | 'Z': 611 2024 | 'bracketleft': 278 2025 | 'backslash': 278 2026 | 'bracketright': 278 2027 | 'asciicircum': 469 2028 | 'underscore': 556 2029 | 'quoteleft': 222 2030 | 'a': 556 2031 | 'b': 556 2032 | 'c': 500 2033 | 'd': 556 2034 | 'e': 556 2035 | 'f': 278 2036 | 'g': 556 2037 | 'h': 556 2038 | 'i': 222 2039 | 'j': 222 2040 | 'k': 500 2041 | 'l': 222 2042 | 'm': 833 2043 | 'n': 556 2044 | 'o': 556 2045 | 'p': 556 2046 | 'q': 556 2047 | 'r': 333 2048 | 's': 500 2049 | 't': 278 2050 | 'u': 556 2051 | 'v': 500 2052 | 'w': 722 2053 | 'x': 500 2054 | 'y': 500 2055 | 'z': 500 2056 | 'braceleft': 334 2057 | 'bar': 260 2058 | 'braceright': 334 2059 | 'asciitilde': 584 2060 | 'exclamdown': 333 2061 | 'cent': 556 2062 | 'sterling': 556 2063 | 'fraction': 167 2064 | 'yen': 556 2065 | 'florin': 556 2066 | 'section': 556 2067 | 'currency': 556 2068 | 'quotesingle': 191 2069 | 'quotedblleft': 333 2070 | 'guillemotleft': 556 2071 | 'guilsinglleft': 333 2072 | 'guilsinglright': 333 2073 | 'fi': 500 2074 | 'fl': 500 2075 | 'endash': 556 2076 | 'dagger': 556 2077 | 'daggerdbl': 556 2078 | 'periodcentered': 278 2079 | 'paragraph': 537 2080 | 'bullet': 350 2081 | 'quotesinglbase': 222 2082 | 'quotedblbase': 333 2083 | 'quotedblright': 333 2084 | 'guillemotright': 556 2085 | 'ellipsis': 1000 2086 | 'perthousand': 1000 2087 | 'questiondown': 611 2088 | 'grave': 333 2089 | 'acute': 333 2090 | 'circumflex': 333 2091 | 'tilde': 333 2092 | 'macron': 333 2093 | 'breve': 333 2094 | 'dotaccent': 333 2095 | 'dieresis': 333 2096 | 'ring': 333 2097 | 'cedilla': 333 2098 | 'hungarumlaut': 333 2099 | 'ogonek': 333 2100 | 'caron': 333 2101 | 'emdash': 1000 2102 | 'AE': 1000 2103 | 'ordfeminine': 370 2104 | 'Lslash': 556 2105 | 'Oslash': 778 2106 | 'OE': 1000 2107 | 'ordmasculine': 365 2108 | 'ae': 889 2109 | 'dotlessi': 278 2110 | 'lslash': 222 2111 | 'oslash': 611 2112 | 'oe': 944 2113 | 'germandbls': 611 2114 | 'Idieresis': 278 2115 | 'eacute': 556 2116 | 'abreve': 556 2117 | 'uhungarumlaut': 556 2118 | 'ecaron': 556 2119 | 'Ydieresis': 667 2120 | 'divide': 584 2121 | 'Yacute': 667 2122 | 'Acircumflex': 667 2123 | 'aacute': 556 2124 | 'Ucircumflex': 722 2125 | 'yacute': 500 2126 | 'scommaaccent': 500 2127 | 'ecircumflex': 556 2128 | 'Uring': 722 2129 | 'Udieresis': 722 2130 | 'aogonek': 556 2131 | 'Uacute': 722 2132 | 'uogonek': 556 2133 | 'Edieresis': 667 2134 | 'Dcroat': 722 2135 | 'commaaccent': 250 2136 | 'copyright': 737 2137 | 'Emacron': 667 2138 | 'ccaron': 500 2139 | 'aring': 556 2140 | 'Ncommaaccent': 722 2141 | 'lacute': 222 2142 | 'agrave': 556 2143 | 'Tcommaaccent': 611 2144 | 'Cacute': 722 2145 | 'atilde': 556 2146 | 'Edotaccent': 667 2147 | 'scaron': 500 2148 | 'scedilla': 500 2149 | 'iacute': 278 2150 | 'lozenge': 471 2151 | 'Rcaron': 722 2152 | 'Gcommaaccent': 778 2153 | 'ucircumflex': 556 2154 | 'acircumflex': 556 2155 | 'Amacron': 667 2156 | 'rcaron': 333 2157 | 'ccedilla': 500 2158 | 'Zdotaccent': 611 2159 | 'Thorn': 667 2160 | 'Omacron': 778 2161 | 'Racute': 722 2162 | 'Sacute': 667 2163 | 'dcaron': 643 2164 | 'Umacron': 722 2165 | 'uring': 556 2166 | 'threesuperior': 333 2167 | 'Ograve': 778 2168 | 'Agrave': 667 2169 | 'Abreve': 667 2170 | 'multiply': 584 2171 | 'uacute': 556 2172 | 'Tcaron': 611 2173 | 'partialdiff': 476 2174 | 'ydieresis': 500 2175 | 'Nacute': 722 2176 | 'icircumflex': 278 2177 | 'Ecircumflex': 667 2178 | 'adieresis': 556 2179 | 'edieresis': 556 2180 | 'cacute': 500 2181 | 'nacute': 556 2182 | 'umacron': 556 2183 | 'Ncaron': 722 2184 | 'Iacute': 278 2185 | 'plusminus': 584 2186 | 'brokenbar': 260 2187 | 'registered': 737 2188 | 'Gbreve': 778 2189 | 'Idotaccent': 278 2190 | 'summation': 600 2191 | 'Egrave': 667 2192 | 'racute': 333 2193 | 'omacron': 556 2194 | 'Zacute': 611 2195 | 'Zcaron': 611 2196 | 'greaterequal': 549 2197 | 'Eth': 722 2198 | 'Ccedilla': 722 2199 | 'lcommaaccent': 222 2200 | 'tcaron': 317 2201 | 'eogonek': 556 2202 | 'Uogonek': 722 2203 | 'Aacute': 667 2204 | 'Adieresis': 667 2205 | 'egrave': 556 2206 | 'zacute': 500 2207 | 'iogonek': 222 2208 | 'Oacute': 778 2209 | 'oacute': 556 2210 | 'amacron': 556 2211 | 'sacute': 500 2212 | 'idieresis': 278 2213 | 'Ocircumflex': 778 2214 | 'Ugrave': 722 2215 | 'Delta': 612 2216 | 'thorn': 556 2217 | 'twosuperior': 333 2218 | 'Odieresis': 778 2219 | 'mu': 556 2220 | 'igrave': 278 2221 | 'ohungarumlaut': 556 2222 | 'Eogonek': 667 2223 | 'dcroat': 556 2224 | 'threequarters': 834 2225 | 'Scedilla': 667 2226 | 'lcaron': 299 2227 | 'Kcommaaccent': 667 2228 | 'Lacute': 556 2229 | 'trademark': 1000 2230 | 'edotaccent': 556 2231 | 'Igrave': 278 2232 | 'Imacron': 278 2233 | 'Lcaron': 556 2234 | 'onehalf': 834 2235 | 'lessequal': 549 2236 | 'ocircumflex': 556 2237 | 'ntilde': 556 2238 | 'Uhungarumlaut': 722 2239 | 'Eacute': 667 2240 | 'emacron': 556 2241 | 'gbreve': 556 2242 | 'onequarter': 834 2243 | 'Scaron': 667 2244 | 'Scommaaccent': 667 2245 | 'Ohungarumlaut': 778 2246 | 'degree': 400 2247 | 'ograve': 556 2248 | 'Ccaron': 722 2249 | 'ugrave': 556 2250 | 'radical': 453 2251 | 'Dcaron': 722 2252 | 'rcommaaccent': 333 2253 | 'Ntilde': 722 2254 | 'otilde': 556 2255 | 'Rcommaaccent': 722 2256 | 'Lcommaaccent': 556 2257 | 'Atilde': 667 2258 | 'Aogonek': 667 2259 | 'Aring': 667 2260 | 'Otilde': 778 2261 | 'zdotaccent': 500 2262 | 'Ecaron': 667 2263 | 'Iogonek': 278 2264 | 'kcommaaccent': 500 2265 | 'minus': 584 2266 | 'Icircumflex': 278 2267 | 'ncaron': 556 2268 | 'tcommaaccent': 278 2269 | 'logicalnot': 584 2270 | 'odieresis': 556 2271 | 'udieresis': 556 2272 | 'notequal': 549 2273 | 'gcommaaccent': 556 2274 | 'eth': 556 2275 | 'zcaron': 500 2276 | 'ncommaaccent': 556 2277 | 'onesuperior': 333 2278 | 'imacron': 278 2279 | 'Euro': 556 2280 | } 2281 | 'Helvetica': { 2282 | 'space': 278 2283 | 'exclam': 278 2284 | 'quotedbl': 355 2285 | 'numbersign': 556 2286 | 'dollar': 556 2287 | 'percent': 889 2288 | 'ampersand': 667 2289 | 'quoteright': 222 2290 | 'parenleft': 333 2291 | 'parenright': 333 2292 | 'asterisk': 389 2293 | 'plus': 584 2294 | 'comma': 278 2295 | 'hyphen': 333 2296 | 'period': 278 2297 | 'slash': 278 2298 | 'zero': 556 2299 | 'one': 556 2300 | 'two': 556 2301 | 'three': 556 2302 | 'four': 556 2303 | 'five': 556 2304 | 'six': 556 2305 | 'seven': 556 2306 | 'eight': 556 2307 | 'nine': 556 2308 | 'colon': 278 2309 | 'semicolon': 278 2310 | 'less': 584 2311 | 'equal': 584 2312 | 'greater': 584 2313 | 'question': 556 2314 | 'at': 1015 2315 | 'A': 667 2316 | 'B': 667 2317 | 'C': 722 2318 | 'D': 722 2319 | 'E': 667 2320 | 'F': 611 2321 | 'G': 778 2322 | 'H': 722 2323 | 'I': 278 2324 | 'J': 500 2325 | 'K': 667 2326 | 'L': 556 2327 | 'M': 833 2328 | 'N': 722 2329 | 'O': 778 2330 | 'P': 667 2331 | 'Q': 778 2332 | 'R': 722 2333 | 'S': 667 2334 | 'T': 611 2335 | 'U': 722 2336 | 'V': 667 2337 | 'W': 944 2338 | 'X': 667 2339 | 'Y': 667 2340 | 'Z': 611 2341 | 'bracketleft': 278 2342 | 'backslash': 278 2343 | 'bracketright': 278 2344 | 'asciicircum': 469 2345 | 'underscore': 556 2346 | 'quoteleft': 222 2347 | 'a': 556 2348 | 'b': 556 2349 | 'c': 500 2350 | 'd': 556 2351 | 'e': 556 2352 | 'f': 278 2353 | 'g': 556 2354 | 'h': 556 2355 | 'i': 222 2356 | 'j': 222 2357 | 'k': 500 2358 | 'l': 222 2359 | 'm': 833 2360 | 'n': 556 2361 | 'o': 556 2362 | 'p': 556 2363 | 'q': 556 2364 | 'r': 333 2365 | 's': 500 2366 | 't': 278 2367 | 'u': 556 2368 | 'v': 500 2369 | 'w': 722 2370 | 'x': 500 2371 | 'y': 500 2372 | 'z': 500 2373 | 'braceleft': 334 2374 | 'bar': 260 2375 | 'braceright': 334 2376 | 'asciitilde': 584 2377 | 'exclamdown': 333 2378 | 'cent': 556 2379 | 'sterling': 556 2380 | 'fraction': 167 2381 | 'yen': 556 2382 | 'florin': 556 2383 | 'section': 556 2384 | 'currency': 556 2385 | 'quotesingle': 191 2386 | 'quotedblleft': 333 2387 | 'guillemotleft': 556 2388 | 'guilsinglleft': 333 2389 | 'guilsinglright': 333 2390 | 'fi': 500 2391 | 'fl': 500 2392 | 'endash': 556 2393 | 'dagger': 556 2394 | 'daggerdbl': 556 2395 | 'periodcentered': 278 2396 | 'paragraph': 537 2397 | 'bullet': 350 2398 | 'quotesinglbase': 222 2399 | 'quotedblbase': 333 2400 | 'quotedblright': 333 2401 | 'guillemotright': 556 2402 | 'ellipsis': 1000 2403 | 'perthousand': 1000 2404 | 'questiondown': 611 2405 | 'grave': 333 2406 | 'acute': 333 2407 | 'circumflex': 333 2408 | 'tilde': 333 2409 | 'macron': 333 2410 | 'breve': 333 2411 | 'dotaccent': 333 2412 | 'dieresis': 333 2413 | 'ring': 333 2414 | 'cedilla': 333 2415 | 'hungarumlaut': 333 2416 | 'ogonek': 333 2417 | 'caron': 333 2418 | 'emdash': 1000 2419 | 'AE': 1000 2420 | 'ordfeminine': 370 2421 | 'Lslash': 556 2422 | 'Oslash': 778 2423 | 'OE': 1000 2424 | 'ordmasculine': 365 2425 | 'ae': 889 2426 | 'dotlessi': 278 2427 | 'lslash': 222 2428 | 'oslash': 611 2429 | 'oe': 944 2430 | 'germandbls': 611 2431 | 'Idieresis': 278 2432 | 'eacute': 556 2433 | 'abreve': 556 2434 | 'uhungarumlaut': 556 2435 | 'ecaron': 556 2436 | 'Ydieresis': 667 2437 | 'divide': 584 2438 | 'Yacute': 667 2439 | 'Acircumflex': 667 2440 | 'aacute': 556 2441 | 'Ucircumflex': 722 2442 | 'yacute': 500 2443 | 'scommaaccent': 500 2444 | 'ecircumflex': 556 2445 | 'Uring': 722 2446 | 'Udieresis': 722 2447 | 'aogonek': 556 2448 | 'Uacute': 722 2449 | 'uogonek': 556 2450 | 'Edieresis': 667 2451 | 'Dcroat': 722 2452 | 'commaaccent': 250 2453 | 'copyright': 737 2454 | 'Emacron': 667 2455 | 'ccaron': 500 2456 | 'aring': 556 2457 | 'Ncommaaccent': 722 2458 | 'lacute': 222 2459 | 'agrave': 556 2460 | 'Tcommaaccent': 611 2461 | 'Cacute': 722 2462 | 'atilde': 556 2463 | 'Edotaccent': 667 2464 | 'scaron': 500 2465 | 'scedilla': 500 2466 | 'iacute': 278 2467 | 'lozenge': 471 2468 | 'Rcaron': 722 2469 | 'Gcommaaccent': 778 2470 | 'ucircumflex': 556 2471 | 'acircumflex': 556 2472 | 'Amacron': 667 2473 | 'rcaron': 333 2474 | 'ccedilla': 500 2475 | 'Zdotaccent': 611 2476 | 'Thorn': 667 2477 | 'Omacron': 778 2478 | 'Racute': 722 2479 | 'Sacute': 667 2480 | 'dcaron': 643 2481 | 'Umacron': 722 2482 | 'uring': 556 2483 | 'threesuperior': 333 2484 | 'Ograve': 778 2485 | 'Agrave': 667 2486 | 'Abreve': 667 2487 | 'multiply': 584 2488 | 'uacute': 556 2489 | 'Tcaron': 611 2490 | 'partialdiff': 476 2491 | 'ydieresis': 500 2492 | 'Nacute': 722 2493 | 'icircumflex': 278 2494 | 'Ecircumflex': 667 2495 | 'adieresis': 556 2496 | 'edieresis': 556 2497 | 'cacute': 500 2498 | 'nacute': 556 2499 | 'umacron': 556 2500 | 'Ncaron': 722 2501 | 'Iacute': 278 2502 | 'plusminus': 584 2503 | 'brokenbar': 260 2504 | 'registered': 737 2505 | 'Gbreve': 778 2506 | 'Idotaccent': 278 2507 | 'summation': 600 2508 | 'Egrave': 667 2509 | 'racute': 333 2510 | 'omacron': 556 2511 | 'Zacute': 611 2512 | 'Zcaron': 611 2513 | 'greaterequal': 549 2514 | 'Eth': 722 2515 | 'Ccedilla': 722 2516 | 'lcommaaccent': 222 2517 | 'tcaron': 317 2518 | 'eogonek': 556 2519 | 'Uogonek': 722 2520 | 'Aacute': 667 2521 | 'Adieresis': 667 2522 | 'egrave': 556 2523 | 'zacute': 500 2524 | 'iogonek': 222 2525 | 'Oacute': 778 2526 | 'oacute': 556 2527 | 'amacron': 556 2528 | 'sacute': 500 2529 | 'idieresis': 278 2530 | 'Ocircumflex': 778 2531 | 'Ugrave': 722 2532 | 'Delta': 612 2533 | 'thorn': 556 2534 | 'twosuperior': 333 2535 | 'Odieresis': 778 2536 | 'mu': 556 2537 | 'igrave': 278 2538 | 'ohungarumlaut': 556 2539 | 'Eogonek': 667 2540 | 'dcroat': 556 2541 | 'threequarters': 834 2542 | 'Scedilla': 667 2543 | 'lcaron': 299 2544 | 'Kcommaaccent': 667 2545 | 'Lacute': 556 2546 | 'trademark': 1000 2547 | 'edotaccent': 556 2548 | 'Igrave': 278 2549 | 'Imacron': 278 2550 | 'Lcaron': 556 2551 | 'onehalf': 834 2552 | 'lessequal': 549 2553 | 'ocircumflex': 556 2554 | 'ntilde': 556 2555 | 'Uhungarumlaut': 722 2556 | 'Eacute': 667 2557 | 'emacron': 556 2558 | 'gbreve': 556 2559 | 'onequarter': 834 2560 | 'Scaron': 667 2561 | 'Scommaaccent': 667 2562 | 'Ohungarumlaut': 778 2563 | 'degree': 400 2564 | 'ograve': 556 2565 | 'Ccaron': 722 2566 | 'ugrave': 556 2567 | 'radical': 453 2568 | 'Dcaron': 722 2569 | 'rcommaaccent': 333 2570 | 'Ntilde': 722 2571 | 'otilde': 556 2572 | 'Rcommaaccent': 722 2573 | 'Lcommaaccent': 556 2574 | 'Atilde': 667 2575 | 'Aogonek': 667 2576 | 'Aring': 667 2577 | 'Otilde': 778 2578 | 'zdotaccent': 500 2579 | 'Ecaron': 667 2580 | 'Iogonek': 278 2581 | 'kcommaaccent': 500 2582 | 'minus': 584 2583 | 'Icircumflex': 278 2584 | 'ncaron': 556 2585 | 'tcommaaccent': 278 2586 | 'logicalnot': 584 2587 | 'odieresis': 556 2588 | 'udieresis': 556 2589 | 'notequal': 549 2590 | 'gcommaaccent': 556 2591 | 'eth': 556 2592 | 'zcaron': 500 2593 | 'ncommaaccent': 556 2594 | 'onesuperior': 333 2595 | 'imacron': 278 2596 | 'Euro': 556 2597 | } 2598 | 'Symbol': { 2599 | 'space': 250 2600 | 'exclam': 333 2601 | 'universal': 713 2602 | 'numbersign': 500 2603 | 'existential': 549 2604 | 'percent': 833 2605 | 'ampersand': 778 2606 | 'suchthat': 439 2607 | 'parenleft': 333 2608 | 'parenright': 333 2609 | 'asteriskmath': 500 2610 | 'plus': 549 2611 | 'comma': 250 2612 | 'minus': 549 2613 | 'period': 250 2614 | 'slash': 278 2615 | 'zero': 500 2616 | 'one': 500 2617 | 'two': 500 2618 | 'three': 500 2619 | 'four': 500 2620 | 'five': 500 2621 | 'six': 500 2622 | 'seven': 500 2623 | 'eight': 500 2624 | 'nine': 500 2625 | 'colon': 278 2626 | 'semicolon': 278 2627 | 'less': 549 2628 | 'equal': 549 2629 | 'greater': 549 2630 | 'question': 444 2631 | 'congruent': 549 2632 | 'Alpha': 722 2633 | 'Beta': 667 2634 | 'Chi': 722 2635 | 'Delta': 612 2636 | 'Epsilon': 611 2637 | 'Phi': 763 2638 | 'Gamma': 603 2639 | 'Eta': 722 2640 | 'Iota': 333 2641 | 'theta1': 631 2642 | 'Kappa': 722 2643 | 'Lambda': 686 2644 | 'Mu': 889 2645 | 'Nu': 722 2646 | 'Omicron': 722 2647 | 'Pi': 768 2648 | 'Theta': 741 2649 | 'Rho': 556 2650 | 'Sigma': 592 2651 | 'Tau': 611 2652 | 'Upsilon': 690 2653 | 'sigma1': 439 2654 | 'Omega': 768 2655 | 'Xi': 645 2656 | 'Psi': 795 2657 | 'Zeta': 611 2658 | 'bracketleft': 333 2659 | 'therefore': 863 2660 | 'bracketright': 333 2661 | 'perpendicular': 658 2662 | 'underscore': 500 2663 | 'radicalex': 500 2664 | 'alpha': 631 2665 | 'beta': 549 2666 | 'chi': 549 2667 | 'delta': 494 2668 | 'epsilon': 439 2669 | 'phi': 521 2670 | 'gamma': 411 2671 | 'eta': 603 2672 | 'iota': 329 2673 | 'phi1': 603 2674 | 'kappa': 549 2675 | 'lambda': 549 2676 | 'mu': 576 2677 | 'nu': 521 2678 | 'omicron': 549 2679 | 'pi': 549 2680 | 'theta': 521 2681 | 'rho': 549 2682 | 'sigma': 603 2683 | 'tau': 439 2684 | 'upsilon': 576 2685 | 'omega1': 713 2686 | 'omega': 686 2687 | 'xi': 493 2688 | 'psi': 686 2689 | 'zeta': 494 2690 | 'braceleft': 480 2691 | 'bar': 200 2692 | 'braceright': 480 2693 | 'similar': 549 2694 | 'Euro': 750 2695 | 'Upsilon1': 620 2696 | 'minute': 247 2697 | 'lessequal': 549 2698 | 'fraction': 167 2699 | 'infinity': 713 2700 | 'florin': 500 2701 | 'club': 753 2702 | 'diamond': 753 2703 | 'heart': 753 2704 | 'spade': 753 2705 | 'arrowboth': 1042 2706 | 'arrowleft': 987 2707 | 'arrowup': 603 2708 | 'arrowright': 987 2709 | 'arrowdown': 603 2710 | 'degree': 400 2711 | 'plusminus': 549 2712 | 'second': 411 2713 | 'greaterequal': 549 2714 | 'multiply': 549 2715 | 'proportional': 713 2716 | 'partialdiff': 494 2717 | 'bullet': 460 2718 | 'divide': 549 2719 | 'notequal': 549 2720 | 'equivalence': 549 2721 | 'approxequal': 549 2722 | 'ellipsis': 1000 2723 | 'arrowvertex': 603 2724 | 'arrowhorizex': 1000 2725 | 'carriagereturn': 658 2726 | 'aleph': 823 2727 | 'Ifraktur': 686 2728 | 'Rfraktur': 795 2729 | 'weierstrass': 987 2730 | 'circlemultiply': 768 2731 | 'circleplus': 768 2732 | 'emptyset': 823 2733 | 'intersection': 768 2734 | 'union': 768 2735 | 'propersuperset': 713 2736 | 'reflexsuperset': 713 2737 | 'notsubset': 713 2738 | 'propersubset': 713 2739 | 'reflexsubset': 713 2740 | 'element': 713 2741 | 'notelement': 713 2742 | 'angle': 768 2743 | 'gradient': 713 2744 | 'registerserif': 790 2745 | 'copyrightserif': 790 2746 | 'trademarkserif': 890 2747 | 'product': 823 2748 | 'radical': 549 2749 | 'dotmath': 250 2750 | 'logicalnot': 713 2751 | 'logicaland': 603 2752 | 'logicalor': 603 2753 | 'arrowdblboth': 1042 2754 | 'arrowdblleft': 987 2755 | 'arrowdblup': 603 2756 | 'arrowdblright': 987 2757 | 'arrowdbldown': 603 2758 | 'lozenge': 494 2759 | 'angleleft': 329 2760 | 'registersans': 790 2761 | 'copyrightsans': 790 2762 | 'trademarksans': 786 2763 | 'summation': 713 2764 | 'parenlefttp': 384 2765 | 'parenleftex': 384 2766 | 'parenleftbt': 384 2767 | 'bracketlefttp': 384 2768 | 'bracketleftex': 384 2769 | 'bracketleftbt': 384 2770 | 'bracelefttp': 494 2771 | 'braceleftmid': 494 2772 | 'braceleftbt': 494 2773 | 'braceex': 494 2774 | 'angleright': 329 2775 | 'integral': 274 2776 | 'integraltp': 686 2777 | 'integralex': 686 2778 | 'integralbt': 686 2779 | 'parenrighttp': 384 2780 | 'parenrightex': 384 2781 | 'parenrightbt': 384 2782 | 'bracketrighttp': 384 2783 | 'bracketrightex': 384 2784 | 'bracketrightbt': 384 2785 | 'bracerighttp': 494 2786 | 'bracerightmid': 494 2787 | 'bracerightbt': 494 2788 | 'apple': 790 2789 | } 2790 | 'Times-Bold': { 2791 | 'space': 250 2792 | 'exclam': 333 2793 | 'quotedbl': 555 2794 | 'numbersign': 500 2795 | 'dollar': 500 2796 | 'percent': 1000 2797 | 'ampersand': 833 2798 | 'quoteright': 333 2799 | 'parenleft': 333 2800 | 'parenright': 333 2801 | 'asterisk': 500 2802 | 'plus': 570 2803 | 'comma': 250 2804 | 'hyphen': 333 2805 | 'period': 250 2806 | 'slash': 278 2807 | 'zero': 500 2808 | 'one': 500 2809 | 'two': 500 2810 | 'three': 500 2811 | 'four': 500 2812 | 'five': 500 2813 | 'six': 500 2814 | 'seven': 500 2815 | 'eight': 500 2816 | 'nine': 500 2817 | 'colon': 333 2818 | 'semicolon': 333 2819 | 'less': 570 2820 | 'equal': 570 2821 | 'greater': 570 2822 | 'question': 500 2823 | 'at': 930 2824 | 'A': 722 2825 | 'B': 667 2826 | 'C': 722 2827 | 'D': 722 2828 | 'E': 667 2829 | 'F': 611 2830 | 'G': 778 2831 | 'H': 778 2832 | 'I': 389 2833 | 'J': 500 2834 | 'K': 778 2835 | 'L': 667 2836 | 'M': 944 2837 | 'N': 722 2838 | 'O': 778 2839 | 'P': 611 2840 | 'Q': 778 2841 | 'R': 722 2842 | 'S': 556 2843 | 'T': 667 2844 | 'U': 722 2845 | 'V': 722 2846 | 'W': 1000 2847 | 'X': 722 2848 | 'Y': 722 2849 | 'Z': 667 2850 | 'bracketleft': 333 2851 | 'backslash': 278 2852 | 'bracketright': 333 2853 | 'asciicircum': 581 2854 | 'underscore': 500 2855 | 'quoteleft': 333 2856 | 'a': 500 2857 | 'b': 556 2858 | 'c': 444 2859 | 'd': 556 2860 | 'e': 444 2861 | 'f': 333 2862 | 'g': 500 2863 | 'h': 556 2864 | 'i': 278 2865 | 'j': 333 2866 | 'k': 556 2867 | 'l': 278 2868 | 'm': 833 2869 | 'n': 556 2870 | 'o': 500 2871 | 'p': 556 2872 | 'q': 556 2873 | 'r': 444 2874 | 's': 389 2875 | 't': 333 2876 | 'u': 556 2877 | 'v': 500 2878 | 'w': 722 2879 | 'x': 500 2880 | 'y': 500 2881 | 'z': 444 2882 | 'braceleft': 394 2883 | 'bar': 220 2884 | 'braceright': 394 2885 | 'asciitilde': 520 2886 | 'exclamdown': 333 2887 | 'cent': 500 2888 | 'sterling': 500 2889 | 'fraction': 167 2890 | 'yen': 500 2891 | 'florin': 500 2892 | 'section': 500 2893 | 'currency': 500 2894 | 'quotesingle': 278 2895 | 'quotedblleft': 500 2896 | 'guillemotleft': 500 2897 | 'guilsinglleft': 333 2898 | 'guilsinglright': 333 2899 | 'fi': 556 2900 | 'fl': 556 2901 | 'endash': 500 2902 | 'dagger': 500 2903 | 'daggerdbl': 500 2904 | 'periodcentered': 250 2905 | 'paragraph': 540 2906 | 'bullet': 350 2907 | 'quotesinglbase': 333 2908 | 'quotedblbase': 500 2909 | 'quotedblright': 500 2910 | 'guillemotright': 500 2911 | 'ellipsis': 1000 2912 | 'perthousand': 1000 2913 | 'questiondown': 500 2914 | 'grave': 333 2915 | 'acute': 333 2916 | 'circumflex': 333 2917 | 'tilde': 333 2918 | 'macron': 333 2919 | 'breve': 333 2920 | 'dotaccent': 333 2921 | 'dieresis': 333 2922 | 'ring': 333 2923 | 'cedilla': 333 2924 | 'hungarumlaut': 333 2925 | 'ogonek': 333 2926 | 'caron': 333 2927 | 'emdash': 1000 2928 | 'AE': 1000 2929 | 'ordfeminine': 300 2930 | 'Lslash': 667 2931 | 'Oslash': 778 2932 | 'OE': 1000 2933 | 'ordmasculine': 330 2934 | 'ae': 722 2935 | 'dotlessi': 278 2936 | 'lslash': 278 2937 | 'oslash': 500 2938 | 'oe': 722 2939 | 'germandbls': 556 2940 | 'Idieresis': 389 2941 | 'eacute': 444 2942 | 'abreve': 500 2943 | 'uhungarumlaut': 556 2944 | 'ecaron': 444 2945 | 'Ydieresis': 722 2946 | 'divide': 570 2947 | 'Yacute': 722 2948 | 'Acircumflex': 722 2949 | 'aacute': 500 2950 | 'Ucircumflex': 722 2951 | 'yacute': 500 2952 | 'scommaaccent': 389 2953 | 'ecircumflex': 444 2954 | 'Uring': 722 2955 | 'Udieresis': 722 2956 | 'aogonek': 500 2957 | 'Uacute': 722 2958 | 'uogonek': 556 2959 | 'Edieresis': 667 2960 | 'Dcroat': 722 2961 | 'commaaccent': 250 2962 | 'copyright': 747 2963 | 'Emacron': 667 2964 | 'ccaron': 444 2965 | 'aring': 500 2966 | 'Ncommaaccent': 722 2967 | 'lacute': 278 2968 | 'agrave': 500 2969 | 'Tcommaaccent': 667 2970 | 'Cacute': 722 2971 | 'atilde': 500 2972 | 'Edotaccent': 667 2973 | 'scaron': 389 2974 | 'scedilla': 389 2975 | 'iacute': 278 2976 | 'lozenge': 494 2977 | 'Rcaron': 722 2978 | 'Gcommaaccent': 778 2979 | 'ucircumflex': 556 2980 | 'acircumflex': 500 2981 | 'Amacron': 722 2982 | 'rcaron': 444 2983 | 'ccedilla': 444 2984 | 'Zdotaccent': 667 2985 | 'Thorn': 611 2986 | 'Omacron': 778 2987 | 'Racute': 722 2988 | 'Sacute': 556 2989 | 'dcaron': 672 2990 | 'Umacron': 722 2991 | 'uring': 556 2992 | 'threesuperior': 300 2993 | 'Ograve': 778 2994 | 'Agrave': 722 2995 | 'Abreve': 722 2996 | 'multiply': 570 2997 | 'uacute': 556 2998 | 'Tcaron': 667 2999 | 'partialdiff': 494 3000 | 'ydieresis': 500 3001 | 'Nacute': 722 3002 | 'icircumflex': 278 3003 | 'Ecircumflex': 667 3004 | 'adieresis': 500 3005 | 'edieresis': 444 3006 | 'cacute': 444 3007 | 'nacute': 556 3008 | 'umacron': 556 3009 | 'Ncaron': 722 3010 | 'Iacute': 389 3011 | 'plusminus': 570 3012 | 'brokenbar': 220 3013 | 'registered': 747 3014 | 'Gbreve': 778 3015 | 'Idotaccent': 389 3016 | 'summation': 600 3017 | 'Egrave': 667 3018 | 'racute': 444 3019 | 'omacron': 500 3020 | 'Zacute': 667 3021 | 'Zcaron': 667 3022 | 'greaterequal': 549 3023 | 'Eth': 722 3024 | 'Ccedilla': 722 3025 | 'lcommaaccent': 278 3026 | 'tcaron': 416 3027 | 'eogonek': 444 3028 | 'Uogonek': 722 3029 | 'Aacute': 722 3030 | 'Adieresis': 722 3031 | 'egrave': 444 3032 | 'zacute': 444 3033 | 'iogonek': 278 3034 | 'Oacute': 778 3035 | 'oacute': 500 3036 | 'amacron': 500 3037 | 'sacute': 389 3038 | 'idieresis': 278 3039 | 'Ocircumflex': 778 3040 | 'Ugrave': 722 3041 | 'Delta': 612 3042 | 'thorn': 556 3043 | 'twosuperior': 300 3044 | 'Odieresis': 778 3045 | 'mu': 556 3046 | 'igrave': 278 3047 | 'ohungarumlaut': 500 3048 | 'Eogonek': 667 3049 | 'dcroat': 556 3050 | 'threequarters': 750 3051 | 'Scedilla': 556 3052 | 'lcaron': 394 3053 | 'Kcommaaccent': 778 3054 | 'Lacute': 667 3055 | 'trademark': 1000 3056 | 'edotaccent': 444 3057 | 'Igrave': 389 3058 | 'Imacron': 389 3059 | 'Lcaron': 667 3060 | 'onehalf': 750 3061 | 'lessequal': 549 3062 | 'ocircumflex': 500 3063 | 'ntilde': 556 3064 | 'Uhungarumlaut': 722 3065 | 'Eacute': 667 3066 | 'emacron': 444 3067 | 'gbreve': 500 3068 | 'onequarter': 750 3069 | 'Scaron': 556 3070 | 'Scommaaccent': 556 3071 | 'Ohungarumlaut': 778 3072 | 'degree': 400 3073 | 'ograve': 500 3074 | 'Ccaron': 722 3075 | 'ugrave': 556 3076 | 'radical': 549 3077 | 'Dcaron': 722 3078 | 'rcommaaccent': 444 3079 | 'Ntilde': 722 3080 | 'otilde': 500 3081 | 'Rcommaaccent': 722 3082 | 'Lcommaaccent': 667 3083 | 'Atilde': 722 3084 | 'Aogonek': 722 3085 | 'Aring': 722 3086 | 'Otilde': 778 3087 | 'zdotaccent': 444 3088 | 'Ecaron': 667 3089 | 'Iogonek': 389 3090 | 'kcommaaccent': 556 3091 | 'minus': 570 3092 | 'Icircumflex': 389 3093 | 'ncaron': 556 3094 | 'tcommaaccent': 333 3095 | 'logicalnot': 570 3096 | 'odieresis': 500 3097 | 'udieresis': 556 3098 | 'notequal': 549 3099 | 'gcommaaccent': 500 3100 | 'eth': 500 3101 | 'zcaron': 444 3102 | 'ncommaaccent': 556 3103 | 'onesuperior': 300 3104 | 'imacron': 278 3105 | 'Euro': 500 3106 | } 3107 | 'Times-BoldItalic': { 3108 | 'space': 250 3109 | 'exclam': 389 3110 | 'quotedbl': 555 3111 | 'numbersign': 500 3112 | 'dollar': 500 3113 | 'percent': 833 3114 | 'ampersand': 778 3115 | 'quoteright': 333 3116 | 'parenleft': 333 3117 | 'parenright': 333 3118 | 'asterisk': 500 3119 | 'plus': 570 3120 | 'comma': 250 3121 | 'hyphen': 333 3122 | 'period': 250 3123 | 'slash': 278 3124 | 'zero': 500 3125 | 'one': 500 3126 | 'two': 500 3127 | 'three': 500 3128 | 'four': 500 3129 | 'five': 500 3130 | 'six': 500 3131 | 'seven': 500 3132 | 'eight': 500 3133 | 'nine': 500 3134 | 'colon': 333 3135 | 'semicolon': 333 3136 | 'less': 570 3137 | 'equal': 570 3138 | 'greater': 570 3139 | 'question': 500 3140 | 'at': 832 3141 | 'A': 667 3142 | 'B': 667 3143 | 'C': 667 3144 | 'D': 722 3145 | 'E': 667 3146 | 'F': 667 3147 | 'G': 722 3148 | 'H': 778 3149 | 'I': 389 3150 | 'J': 500 3151 | 'K': 667 3152 | 'L': 611 3153 | 'M': 889 3154 | 'N': 722 3155 | 'O': 722 3156 | 'P': 611 3157 | 'Q': 722 3158 | 'R': 667 3159 | 'S': 556 3160 | 'T': 611 3161 | 'U': 722 3162 | 'V': 667 3163 | 'W': 889 3164 | 'X': 667 3165 | 'Y': 611 3166 | 'Z': 611 3167 | 'bracketleft': 333 3168 | 'backslash': 278 3169 | 'bracketright': 333 3170 | 'asciicircum': 570 3171 | 'underscore': 500 3172 | 'quoteleft': 333 3173 | 'a': 500 3174 | 'b': 500 3175 | 'c': 444 3176 | 'd': 500 3177 | 'e': 444 3178 | 'f': 333 3179 | 'g': 500 3180 | 'h': 556 3181 | 'i': 278 3182 | 'j': 278 3183 | 'k': 500 3184 | 'l': 278 3185 | 'm': 778 3186 | 'n': 556 3187 | 'o': 500 3188 | 'p': 500 3189 | 'q': 500 3190 | 'r': 389 3191 | 's': 389 3192 | 't': 278 3193 | 'u': 556 3194 | 'v': 444 3195 | 'w': 667 3196 | 'x': 500 3197 | 'y': 444 3198 | 'z': 389 3199 | 'braceleft': 348 3200 | 'bar': 220 3201 | 'braceright': 348 3202 | 'asciitilde': 570 3203 | 'exclamdown': 389 3204 | 'cent': 500 3205 | 'sterling': 500 3206 | 'fraction': 167 3207 | 'yen': 500 3208 | 'florin': 500 3209 | 'section': 500 3210 | 'currency': 500 3211 | 'quotesingle': 278 3212 | 'quotedblleft': 500 3213 | 'guillemotleft': 500 3214 | 'guilsinglleft': 333 3215 | 'guilsinglright': 333 3216 | 'fi': 556 3217 | 'fl': 556 3218 | 'endash': 500 3219 | 'dagger': 500 3220 | 'daggerdbl': 500 3221 | 'periodcentered': 250 3222 | 'paragraph': 500 3223 | 'bullet': 350 3224 | 'quotesinglbase': 333 3225 | 'quotedblbase': 500 3226 | 'quotedblright': 500 3227 | 'guillemotright': 500 3228 | 'ellipsis': 1000 3229 | 'perthousand': 1000 3230 | 'questiondown': 500 3231 | 'grave': 333 3232 | 'acute': 333 3233 | 'circumflex': 333 3234 | 'tilde': 333 3235 | 'macron': 333 3236 | 'breve': 333 3237 | 'dotaccent': 333 3238 | 'dieresis': 333 3239 | 'ring': 333 3240 | 'cedilla': 333 3241 | 'hungarumlaut': 333 3242 | 'ogonek': 333 3243 | 'caron': 333 3244 | 'emdash': 1000 3245 | 'AE': 944 3246 | 'ordfeminine': 266 3247 | 'Lslash': 611 3248 | 'Oslash': 722 3249 | 'OE': 944 3250 | 'ordmasculine': 300 3251 | 'ae': 722 3252 | 'dotlessi': 278 3253 | 'lslash': 278 3254 | 'oslash': 500 3255 | 'oe': 722 3256 | 'germandbls': 500 3257 | 'Idieresis': 389 3258 | 'eacute': 444 3259 | 'abreve': 500 3260 | 'uhungarumlaut': 556 3261 | 'ecaron': 444 3262 | 'Ydieresis': 611 3263 | 'divide': 570 3264 | 'Yacute': 611 3265 | 'Acircumflex': 667 3266 | 'aacute': 500 3267 | 'Ucircumflex': 722 3268 | 'yacute': 444 3269 | 'scommaaccent': 389 3270 | 'ecircumflex': 444 3271 | 'Uring': 722 3272 | 'Udieresis': 722 3273 | 'aogonek': 500 3274 | 'Uacute': 722 3275 | 'uogonek': 556 3276 | 'Edieresis': 667 3277 | 'Dcroat': 722 3278 | 'commaaccent': 250 3279 | 'copyright': 747 3280 | 'Emacron': 667 3281 | 'ccaron': 444 3282 | 'aring': 500 3283 | 'Ncommaaccent': 722 3284 | 'lacute': 278 3285 | 'agrave': 500 3286 | 'Tcommaaccent': 611 3287 | 'Cacute': 667 3288 | 'atilde': 500 3289 | 'Edotaccent': 667 3290 | 'scaron': 389 3291 | 'scedilla': 389 3292 | 'iacute': 278 3293 | 'lozenge': 494 3294 | 'Rcaron': 667 3295 | 'Gcommaaccent': 722 3296 | 'ucircumflex': 556 3297 | 'acircumflex': 500 3298 | 'Amacron': 667 3299 | 'rcaron': 389 3300 | 'ccedilla': 444 3301 | 'Zdotaccent': 611 3302 | 'Thorn': 611 3303 | 'Omacron': 722 3304 | 'Racute': 667 3305 | 'Sacute': 556 3306 | 'dcaron': 608 3307 | 'Umacron': 722 3308 | 'uring': 556 3309 | 'threesuperior': 300 3310 | 'Ograve': 722 3311 | 'Agrave': 667 3312 | 'Abreve': 667 3313 | 'multiply': 570 3314 | 'uacute': 556 3315 | 'Tcaron': 611 3316 | 'partialdiff': 494 3317 | 'ydieresis': 444 3318 | 'Nacute': 722 3319 | 'icircumflex': 278 3320 | 'Ecircumflex': 667 3321 | 'adieresis': 500 3322 | 'edieresis': 444 3323 | 'cacute': 444 3324 | 'nacute': 556 3325 | 'umacron': 556 3326 | 'Ncaron': 722 3327 | 'Iacute': 389 3328 | 'plusminus': 570 3329 | 'brokenbar': 220 3330 | 'registered': 747 3331 | 'Gbreve': 722 3332 | 'Idotaccent': 389 3333 | 'summation': 600 3334 | 'Egrave': 667 3335 | 'racute': 389 3336 | 'omacron': 500 3337 | 'Zacute': 611 3338 | 'Zcaron': 611 3339 | 'greaterequal': 549 3340 | 'Eth': 722 3341 | 'Ccedilla': 667 3342 | 'lcommaaccent': 278 3343 | 'tcaron': 366 3344 | 'eogonek': 444 3345 | 'Uogonek': 722 3346 | 'Aacute': 667 3347 | 'Adieresis': 667 3348 | 'egrave': 444 3349 | 'zacute': 389 3350 | 'iogonek': 278 3351 | 'Oacute': 722 3352 | 'oacute': 500 3353 | 'amacron': 500 3354 | 'sacute': 389 3355 | 'idieresis': 278 3356 | 'Ocircumflex': 722 3357 | 'Ugrave': 722 3358 | 'Delta': 612 3359 | 'thorn': 500 3360 | 'twosuperior': 300 3361 | 'Odieresis': 722 3362 | 'mu': 576 3363 | 'igrave': 278 3364 | 'ohungarumlaut': 500 3365 | 'Eogonek': 667 3366 | 'dcroat': 500 3367 | 'threequarters': 750 3368 | 'Scedilla': 556 3369 | 'lcaron': 382 3370 | 'Kcommaaccent': 667 3371 | 'Lacute': 611 3372 | 'trademark': 1000 3373 | 'edotaccent': 444 3374 | 'Igrave': 389 3375 | 'Imacron': 389 3376 | 'Lcaron': 611 3377 | 'onehalf': 750 3378 | 'lessequal': 549 3379 | 'ocircumflex': 500 3380 | 'ntilde': 556 3381 | 'Uhungarumlaut': 722 3382 | 'Eacute': 667 3383 | 'emacron': 444 3384 | 'gbreve': 500 3385 | 'onequarter': 750 3386 | 'Scaron': 556 3387 | 'Scommaaccent': 556 3388 | 'Ohungarumlaut': 722 3389 | 'degree': 400 3390 | 'ograve': 500 3391 | 'Ccaron': 667 3392 | 'ugrave': 556 3393 | 'radical': 549 3394 | 'Dcaron': 722 3395 | 'rcommaaccent': 389 3396 | 'Ntilde': 722 3397 | 'otilde': 500 3398 | 'Rcommaaccent': 667 3399 | 'Lcommaaccent': 611 3400 | 'Atilde': 667 3401 | 'Aogonek': 667 3402 | 'Aring': 667 3403 | 'Otilde': 722 3404 | 'zdotaccent': 389 3405 | 'Ecaron': 667 3406 | 'Iogonek': 389 3407 | 'kcommaaccent': 500 3408 | 'minus': 606 3409 | 'Icircumflex': 389 3410 | 'ncaron': 556 3411 | 'tcommaaccent': 278 3412 | 'logicalnot': 606 3413 | 'odieresis': 500 3414 | 'udieresis': 556 3415 | 'notequal': 549 3416 | 'gcommaaccent': 500 3417 | 'eth': 500 3418 | 'zcaron': 389 3419 | 'ncommaaccent': 556 3420 | 'onesuperior': 300 3421 | 'imacron': 278 3422 | 'Euro': 500 3423 | } 3424 | 'Times-Italic': { 3425 | 'space': 250 3426 | 'exclam': 333 3427 | 'quotedbl': 420 3428 | 'numbersign': 500 3429 | 'dollar': 500 3430 | 'percent': 833 3431 | 'ampersand': 778 3432 | 'quoteright': 333 3433 | 'parenleft': 333 3434 | 'parenright': 333 3435 | 'asterisk': 500 3436 | 'plus': 675 3437 | 'comma': 250 3438 | 'hyphen': 333 3439 | 'period': 250 3440 | 'slash': 278 3441 | 'zero': 500 3442 | 'one': 500 3443 | 'two': 500 3444 | 'three': 500 3445 | 'four': 500 3446 | 'five': 500 3447 | 'six': 500 3448 | 'seven': 500 3449 | 'eight': 500 3450 | 'nine': 500 3451 | 'colon': 333 3452 | 'semicolon': 333 3453 | 'less': 675 3454 | 'equal': 675 3455 | 'greater': 675 3456 | 'question': 500 3457 | 'at': 920 3458 | 'A': 611 3459 | 'B': 611 3460 | 'C': 667 3461 | 'D': 722 3462 | 'E': 611 3463 | 'F': 611 3464 | 'G': 722 3465 | 'H': 722 3466 | 'I': 333 3467 | 'J': 444 3468 | 'K': 667 3469 | 'L': 556 3470 | 'M': 833 3471 | 'N': 667 3472 | 'O': 722 3473 | 'P': 611 3474 | 'Q': 722 3475 | 'R': 611 3476 | 'S': 500 3477 | 'T': 556 3478 | 'U': 722 3479 | 'V': 611 3480 | 'W': 833 3481 | 'X': 611 3482 | 'Y': 556 3483 | 'Z': 556 3484 | 'bracketleft': 389 3485 | 'backslash': 278 3486 | 'bracketright': 389 3487 | 'asciicircum': 422 3488 | 'underscore': 500 3489 | 'quoteleft': 333 3490 | 'a': 500 3491 | 'b': 500 3492 | 'c': 444 3493 | 'd': 500 3494 | 'e': 444 3495 | 'f': 278 3496 | 'g': 500 3497 | 'h': 500 3498 | 'i': 278 3499 | 'j': 278 3500 | 'k': 444 3501 | 'l': 278 3502 | 'm': 722 3503 | 'n': 500 3504 | 'o': 500 3505 | 'p': 500 3506 | 'q': 500 3507 | 'r': 389 3508 | 's': 389 3509 | 't': 278 3510 | 'u': 500 3511 | 'v': 444 3512 | 'w': 667 3513 | 'x': 444 3514 | 'y': 444 3515 | 'z': 389 3516 | 'braceleft': 400 3517 | 'bar': 275 3518 | 'braceright': 400 3519 | 'asciitilde': 541 3520 | 'exclamdown': 389 3521 | 'cent': 500 3522 | 'sterling': 500 3523 | 'fraction': 167 3524 | 'yen': 500 3525 | 'florin': 500 3526 | 'section': 500 3527 | 'currency': 500 3528 | 'quotesingle': 214 3529 | 'quotedblleft': 556 3530 | 'guillemotleft': 500 3531 | 'guilsinglleft': 333 3532 | 'guilsinglright': 333 3533 | 'fi': 500 3534 | 'fl': 500 3535 | 'endash': 500 3536 | 'dagger': 500 3537 | 'daggerdbl': 500 3538 | 'periodcentered': 250 3539 | 'paragraph': 523 3540 | 'bullet': 350 3541 | 'quotesinglbase': 333 3542 | 'quotedblbase': 556 3543 | 'quotedblright': 556 3544 | 'guillemotright': 500 3545 | 'ellipsis': 889 3546 | 'perthousand': 1000 3547 | 'questiondown': 500 3548 | 'grave': 333 3549 | 'acute': 333 3550 | 'circumflex': 333 3551 | 'tilde': 333 3552 | 'macron': 333 3553 | 'breve': 333 3554 | 'dotaccent': 333 3555 | 'dieresis': 333 3556 | 'ring': 333 3557 | 'cedilla': 333 3558 | 'hungarumlaut': 333 3559 | 'ogonek': 333 3560 | 'caron': 333 3561 | 'emdash': 889 3562 | 'AE': 889 3563 | 'ordfeminine': 276 3564 | 'Lslash': 556 3565 | 'Oslash': 722 3566 | 'OE': 944 3567 | 'ordmasculine': 310 3568 | 'ae': 667 3569 | 'dotlessi': 278 3570 | 'lslash': 278 3571 | 'oslash': 500 3572 | 'oe': 667 3573 | 'germandbls': 500 3574 | 'Idieresis': 333 3575 | 'eacute': 444 3576 | 'abreve': 500 3577 | 'uhungarumlaut': 500 3578 | 'ecaron': 444 3579 | 'Ydieresis': 556 3580 | 'divide': 675 3581 | 'Yacute': 556 3582 | 'Acircumflex': 611 3583 | 'aacute': 500 3584 | 'Ucircumflex': 722 3585 | 'yacute': 444 3586 | 'scommaaccent': 389 3587 | 'ecircumflex': 444 3588 | 'Uring': 722 3589 | 'Udieresis': 722 3590 | 'aogonek': 500 3591 | 'Uacute': 722 3592 | 'uogonek': 500 3593 | 'Edieresis': 611 3594 | 'Dcroat': 722 3595 | 'commaaccent': 250 3596 | 'copyright': 760 3597 | 'Emacron': 611 3598 | 'ccaron': 444 3599 | 'aring': 500 3600 | 'Ncommaaccent': 667 3601 | 'lacute': 278 3602 | 'agrave': 500 3603 | 'Tcommaaccent': 556 3604 | 'Cacute': 667 3605 | 'atilde': 500 3606 | 'Edotaccent': 611 3607 | 'scaron': 389 3608 | 'scedilla': 389 3609 | 'iacute': 278 3610 | 'lozenge': 471 3611 | 'Rcaron': 611 3612 | 'Gcommaaccent': 722 3613 | 'ucircumflex': 500 3614 | 'acircumflex': 500 3615 | 'Amacron': 611 3616 | 'rcaron': 389 3617 | 'ccedilla': 444 3618 | 'Zdotaccent': 556 3619 | 'Thorn': 611 3620 | 'Omacron': 722 3621 | 'Racute': 611 3622 | 'Sacute': 500 3623 | 'dcaron': 544 3624 | 'Umacron': 722 3625 | 'uring': 500 3626 | 'threesuperior': 300 3627 | 'Ograve': 722 3628 | 'Agrave': 611 3629 | 'Abreve': 611 3630 | 'multiply': 675 3631 | 'uacute': 500 3632 | 'Tcaron': 556 3633 | 'partialdiff': 476 3634 | 'ydieresis': 444 3635 | 'Nacute': 667 3636 | 'icircumflex': 278 3637 | 'Ecircumflex': 611 3638 | 'adieresis': 500 3639 | 'edieresis': 444 3640 | 'cacute': 444 3641 | 'nacute': 500 3642 | 'umacron': 500 3643 | 'Ncaron': 667 3644 | 'Iacute': 333 3645 | 'plusminus': 675 3646 | 'brokenbar': 275 3647 | 'registered': 760 3648 | 'Gbreve': 722 3649 | 'Idotaccent': 333 3650 | 'summation': 600 3651 | 'Egrave': 611 3652 | 'racute': 389 3653 | 'omacron': 500 3654 | 'Zacute': 556 3655 | 'Zcaron': 556 3656 | 'greaterequal': 549 3657 | 'Eth': 722 3658 | 'Ccedilla': 667 3659 | 'lcommaaccent': 278 3660 | 'tcaron': 300 3661 | 'eogonek': 444 3662 | 'Uogonek': 722 3663 | 'Aacute': 611 3664 | 'Adieresis': 611 3665 | 'egrave': 444 3666 | 'zacute': 389 3667 | 'iogonek': 278 3668 | 'Oacute': 722 3669 | 'oacute': 500 3670 | 'amacron': 500 3671 | 'sacute': 389 3672 | 'idieresis': 278 3673 | 'Ocircumflex': 722 3674 | 'Ugrave': 722 3675 | 'Delta': 612 3676 | 'thorn': 500 3677 | 'twosuperior': 300 3678 | 'Odieresis': 722 3679 | 'mu': 500 3680 | 'igrave': 278 3681 | 'ohungarumlaut': 500 3682 | 'Eogonek': 611 3683 | 'dcroat': 500 3684 | 'threequarters': 750 3685 | 'Scedilla': 500 3686 | 'lcaron': 300 3687 | 'Kcommaaccent': 667 3688 | 'Lacute': 556 3689 | 'trademark': 980 3690 | 'edotaccent': 444 3691 | 'Igrave': 333 3692 | 'Imacron': 333 3693 | 'Lcaron': 611 3694 | 'onehalf': 750 3695 | 'lessequal': 549 3696 | 'ocircumflex': 500 3697 | 'ntilde': 500 3698 | 'Uhungarumlaut': 722 3699 | 'Eacute': 611 3700 | 'emacron': 444 3701 | 'gbreve': 500 3702 | 'onequarter': 750 3703 | 'Scaron': 500 3704 | 'Scommaaccent': 500 3705 | 'Ohungarumlaut': 722 3706 | 'degree': 400 3707 | 'ograve': 500 3708 | 'Ccaron': 667 3709 | 'ugrave': 500 3710 | 'radical': 453 3711 | 'Dcaron': 722 3712 | 'rcommaaccent': 389 3713 | 'Ntilde': 667 3714 | 'otilde': 500 3715 | 'Rcommaaccent': 611 3716 | 'Lcommaaccent': 556 3717 | 'Atilde': 611 3718 | 'Aogonek': 611 3719 | 'Aring': 611 3720 | 'Otilde': 722 3721 | 'zdotaccent': 389 3722 | 'Ecaron': 611 3723 | 'Iogonek': 333 3724 | 'kcommaaccent': 444 3725 | 'minus': 675 3726 | 'Icircumflex': 333 3727 | 'ncaron': 500 3728 | 'tcommaaccent': 278 3729 | 'logicalnot': 675 3730 | 'odieresis': 500 3731 | 'udieresis': 500 3732 | 'notequal': 549 3733 | 'gcommaaccent': 500 3734 | 'eth': 500 3735 | 'zcaron': 389 3736 | 'ncommaaccent': 500 3737 | 'onesuperior': 300 3738 | 'imacron': 278 3739 | 'Euro': 500 3740 | } 3741 | 'Times-Roman': { 3742 | 'space': 250 3743 | 'exclam': 333 3744 | 'quotedbl': 408 3745 | 'numbersign': 500 3746 | 'dollar': 500 3747 | 'percent': 833 3748 | 'ampersand': 778 3749 | 'quoteright': 333 3750 | 'parenleft': 333 3751 | 'parenright': 333 3752 | 'asterisk': 500 3753 | 'plus': 564 3754 | 'comma': 250 3755 | 'hyphen': 333 3756 | 'period': 250 3757 | 'slash': 278 3758 | 'zero': 500 3759 | 'one': 500 3760 | 'two': 500 3761 | 'three': 500 3762 | 'four': 500 3763 | 'five': 500 3764 | 'six': 500 3765 | 'seven': 500 3766 | 'eight': 500 3767 | 'nine': 500 3768 | 'colon': 278 3769 | 'semicolon': 278 3770 | 'less': 564 3771 | 'equal': 564 3772 | 'greater': 564 3773 | 'question': 444 3774 | 'at': 921 3775 | 'A': 722 3776 | 'B': 667 3777 | 'C': 667 3778 | 'D': 722 3779 | 'E': 611 3780 | 'F': 556 3781 | 'G': 722 3782 | 'H': 722 3783 | 'I': 333 3784 | 'J': 389 3785 | 'K': 722 3786 | 'L': 611 3787 | 'M': 889 3788 | 'N': 722 3789 | 'O': 722 3790 | 'P': 556 3791 | 'Q': 722 3792 | 'R': 667 3793 | 'S': 556 3794 | 'T': 611 3795 | 'U': 722 3796 | 'V': 722 3797 | 'W': 944 3798 | 'X': 722 3799 | 'Y': 722 3800 | 'Z': 611 3801 | 'bracketleft': 333 3802 | 'backslash': 278 3803 | 'bracketright': 333 3804 | 'asciicircum': 469 3805 | 'underscore': 500 3806 | 'quoteleft': 333 3807 | 'a': 444 3808 | 'b': 500 3809 | 'c': 444 3810 | 'd': 500 3811 | 'e': 444 3812 | 'f': 333 3813 | 'g': 500 3814 | 'h': 500 3815 | 'i': 278 3816 | 'j': 278 3817 | 'k': 500 3818 | 'l': 278 3819 | 'm': 778 3820 | 'n': 500 3821 | 'o': 500 3822 | 'p': 500 3823 | 'q': 500 3824 | 'r': 333 3825 | 's': 389 3826 | 't': 278 3827 | 'u': 500 3828 | 'v': 500 3829 | 'w': 722 3830 | 'x': 500 3831 | 'y': 500 3832 | 'z': 444 3833 | 'braceleft': 480 3834 | 'bar': 200 3835 | 'braceright': 480 3836 | 'asciitilde': 541 3837 | 'exclamdown': 333 3838 | 'cent': 500 3839 | 'sterling': 500 3840 | 'fraction': 167 3841 | 'yen': 500 3842 | 'florin': 500 3843 | 'section': 500 3844 | 'currency': 500 3845 | 'quotesingle': 180 3846 | 'quotedblleft': 444 3847 | 'guillemotleft': 500 3848 | 'guilsinglleft': 333 3849 | 'guilsinglright': 333 3850 | 'fi': 556 3851 | 'fl': 556 3852 | 'endash': 500 3853 | 'dagger': 500 3854 | 'daggerdbl': 500 3855 | 'periodcentered': 250 3856 | 'paragraph': 453 3857 | 'bullet': 350 3858 | 'quotesinglbase': 333 3859 | 'quotedblbase': 444 3860 | 'quotedblright': 444 3861 | 'guillemotright': 500 3862 | 'ellipsis': 1000 3863 | 'perthousand': 1000 3864 | 'questiondown': 444 3865 | 'grave': 333 3866 | 'acute': 333 3867 | 'circumflex': 333 3868 | 'tilde': 333 3869 | 'macron': 333 3870 | 'breve': 333 3871 | 'dotaccent': 333 3872 | 'dieresis': 333 3873 | 'ring': 333 3874 | 'cedilla': 333 3875 | 'hungarumlaut': 333 3876 | 'ogonek': 333 3877 | 'caron': 333 3878 | 'emdash': 1000 3879 | 'AE': 889 3880 | 'ordfeminine': 276 3881 | 'Lslash': 611 3882 | 'Oslash': 722 3883 | 'OE': 889 3884 | 'ordmasculine': 310 3885 | 'ae': 667 3886 | 'dotlessi': 278 3887 | 'lslash': 278 3888 | 'oslash': 500 3889 | 'oe': 722 3890 | 'germandbls': 500 3891 | 'Idieresis': 333 3892 | 'eacute': 444 3893 | 'abreve': 444 3894 | 'uhungarumlaut': 500 3895 | 'ecaron': 444 3896 | 'Ydieresis': 722 3897 | 'divide': 564 3898 | 'Yacute': 722 3899 | 'Acircumflex': 722 3900 | 'aacute': 444 3901 | 'Ucircumflex': 722 3902 | 'yacute': 500 3903 | 'scommaaccent': 389 3904 | 'ecircumflex': 444 3905 | 'Uring': 722 3906 | 'Udieresis': 722 3907 | 'aogonek': 444 3908 | 'Uacute': 722 3909 | 'uogonek': 500 3910 | 'Edieresis': 611 3911 | 'Dcroat': 722 3912 | 'commaaccent': 250 3913 | 'copyright': 760 3914 | 'Emacron': 611 3915 | 'ccaron': 444 3916 | 'aring': 444 3917 | 'Ncommaaccent': 722 3918 | 'lacute': 278 3919 | 'agrave': 444 3920 | 'Tcommaaccent': 611 3921 | 'Cacute': 667 3922 | 'atilde': 444 3923 | 'Edotaccent': 611 3924 | 'scaron': 389 3925 | 'scedilla': 389 3926 | 'iacute': 278 3927 | 'lozenge': 471 3928 | 'Rcaron': 667 3929 | 'Gcommaaccent': 722 3930 | 'ucircumflex': 500 3931 | 'acircumflex': 444 3932 | 'Amacron': 722 3933 | 'rcaron': 333 3934 | 'ccedilla': 444 3935 | 'Zdotaccent': 611 3936 | 'Thorn': 556 3937 | 'Omacron': 722 3938 | 'Racute': 667 3939 | 'Sacute': 556 3940 | 'dcaron': 588 3941 | 'Umacron': 722 3942 | 'uring': 500 3943 | 'threesuperior': 300 3944 | 'Ograve': 722 3945 | 'Agrave': 722 3946 | 'Abreve': 722 3947 | 'multiply': 564 3948 | 'uacute': 500 3949 | 'Tcaron': 611 3950 | 'partialdiff': 476 3951 | 'ydieresis': 500 3952 | 'Nacute': 722 3953 | 'icircumflex': 278 3954 | 'Ecircumflex': 611 3955 | 'adieresis': 444 3956 | 'edieresis': 444 3957 | 'cacute': 444 3958 | 'nacute': 500 3959 | 'umacron': 500 3960 | 'Ncaron': 722 3961 | 'Iacute': 333 3962 | 'plusminus': 564 3963 | 'brokenbar': 200 3964 | 'registered': 760 3965 | 'Gbreve': 722 3966 | 'Idotaccent': 333 3967 | 'summation': 600 3968 | 'Egrave': 611 3969 | 'racute': 333 3970 | 'omacron': 500 3971 | 'Zacute': 611 3972 | 'Zcaron': 611 3973 | 'greaterequal': 549 3974 | 'Eth': 722 3975 | 'Ccedilla': 667 3976 | 'lcommaaccent': 278 3977 | 'tcaron': 326 3978 | 'eogonek': 444 3979 | 'Uogonek': 722 3980 | 'Aacute': 722 3981 | 'Adieresis': 722 3982 | 'egrave': 444 3983 | 'zacute': 444 3984 | 'iogonek': 278 3985 | 'Oacute': 722 3986 | 'oacute': 500 3987 | 'amacron': 444 3988 | 'sacute': 389 3989 | 'idieresis': 278 3990 | 'Ocircumflex': 722 3991 | 'Ugrave': 722 3992 | 'Delta': 612 3993 | 'thorn': 500 3994 | 'twosuperior': 300 3995 | 'Odieresis': 722 3996 | 'mu': 500 3997 | 'igrave': 278 3998 | 'ohungarumlaut': 500 3999 | 'Eogonek': 611 4000 | 'dcroat': 500 4001 | 'threequarters': 750 4002 | 'Scedilla': 556 4003 | 'lcaron': 344 4004 | 'Kcommaaccent': 722 4005 | 'Lacute': 611 4006 | 'trademark': 980 4007 | 'edotaccent': 444 4008 | 'Igrave': 333 4009 | 'Imacron': 333 4010 | 'Lcaron': 611 4011 | 'onehalf': 750 4012 | 'lessequal': 549 4013 | 'ocircumflex': 500 4014 | 'ntilde': 500 4015 | 'Uhungarumlaut': 722 4016 | 'Eacute': 611 4017 | 'emacron': 444 4018 | 'gbreve': 500 4019 | 'onequarter': 750 4020 | 'Scaron': 556 4021 | 'Scommaaccent': 556 4022 | 'Ohungarumlaut': 722 4023 | 'degree': 400 4024 | 'ograve': 500 4025 | 'Ccaron': 667 4026 | 'ugrave': 500 4027 | 'radical': 453 4028 | 'Dcaron': 722 4029 | 'rcommaaccent': 333 4030 | 'Ntilde': 722 4031 | 'otilde': 500 4032 | 'Rcommaaccent': 667 4033 | 'Lcommaaccent': 611 4034 | 'Atilde': 722 4035 | 'Aogonek': 722 4036 | 'Aring': 722 4037 | 'Otilde': 722 4038 | 'zdotaccent': 444 4039 | 'Ecaron': 611 4040 | 'Iogonek': 333 4041 | 'kcommaaccent': 500 4042 | 'minus': 564 4043 | 'Icircumflex': 333 4044 | 'ncaron': 500 4045 | 'tcommaaccent': 278 4046 | 'logicalnot': 564 4047 | 'odieresis': 500 4048 | 'udieresis': 500 4049 | 'notequal': 549 4050 | 'gcommaaccent': 500 4051 | 'eth': 500 4052 | 'zcaron': 444 4053 | 'ncommaaccent': 500 4054 | 'onesuperior': 300 4055 | 'imacron': 278 4056 | 'Euro': 500 4057 | } 4058 | 'ZapfDingbats': { 4059 | 'space': 278 4060 | 'a1': 974 4061 | 'a2': 961 4062 | 'a202': 974 4063 | 'a3': 980 4064 | 'a4': 719 4065 | 'a5': 789 4066 | 'a119': 790 4067 | 'a118': 791 4068 | 'a117': 690 4069 | 'a11': 960 4070 | 'a12': 939 4071 | 'a13': 549 4072 | 'a14': 855 4073 | 'a15': 911 4074 | 'a16': 933 4075 | 'a105': 911 4076 | 'a17': 945 4077 | 'a18': 974 4078 | 'a19': 755 4079 | 'a20': 846 4080 | 'a21': 762 4081 | 'a22': 761 4082 | 'a23': 571 4083 | 'a24': 677 4084 | 'a25': 763 4085 | 'a26': 760 4086 | 'a27': 759 4087 | 'a28': 754 4088 | 'a6': 494 4089 | 'a7': 552 4090 | 'a8': 537 4091 | 'a9': 577 4092 | 'a10': 692 4093 | 'a29': 786 4094 | 'a30': 788 4095 | 'a31': 788 4096 | 'a32': 790 4097 | 'a33': 793 4098 | 'a34': 794 4099 | 'a35': 816 4100 | 'a36': 823 4101 | 'a37': 789 4102 | 'a38': 841 4103 | 'a39': 823 4104 | 'a40': 833 4105 | 'a41': 816 4106 | 'a42': 831 4107 | 'a43': 923 4108 | 'a44': 744 4109 | 'a45': 723 4110 | 'a46': 749 4111 | 'a47': 790 4112 | 'a48': 792 4113 | 'a49': 695 4114 | 'a50': 776 4115 | 'a51': 768 4116 | 'a52': 792 4117 | 'a53': 759 4118 | 'a54': 707 4119 | 'a55': 708 4120 | 'a56': 682 4121 | 'a57': 701 4122 | 'a58': 826 4123 | 'a59': 815 4124 | 'a60': 789 4125 | 'a61': 789 4126 | 'a62': 707 4127 | 'a63': 687 4128 | 'a64': 696 4129 | 'a65': 689 4130 | 'a66': 786 4131 | 'a67': 787 4132 | 'a68': 713 4133 | 'a69': 791 4134 | 'a70': 785 4135 | 'a71': 791 4136 | 'a72': 873 4137 | 'a73': 761 4138 | 'a74': 762 4139 | 'a203': 762 4140 | 'a75': 759 4141 | 'a204': 759 4142 | 'a76': 892 4143 | 'a77': 892 4144 | 'a78': 788 4145 | 'a79': 784 4146 | 'a81': 438 4147 | 'a82': 138 4148 | 'a83': 277 4149 | 'a84': 415 4150 | 'a97': 392 4151 | 'a98': 392 4152 | 'a99': 668 4153 | 'a100': 668 4154 | 'a89': 390 4155 | 'a90': 390 4156 | 'a93': 317 4157 | 'a94': 317 4158 | 'a91': 276 4159 | 'a92': 276 4160 | 'a205': 509 4161 | 'a85': 509 4162 | 'a206': 410 4163 | 'a86': 410 4164 | 'a87': 234 4165 | 'a88': 234 4166 | 'a95': 334 4167 | 'a96': 334 4168 | 'a101': 732 4169 | 'a102': 544 4170 | 'a103': 544 4171 | 'a104': 910 4172 | 'a106': 667 4173 | 'a107': 760 4174 | 'a108': 760 4175 | 'a112': 776 4176 | 'a111': 595 4177 | 'a110': 694 4178 | 'a109': 626 4179 | 'a120': 788 4180 | 'a121': 788 4181 | 'a122': 788 4182 | 'a123': 788 4183 | 'a124': 788 4184 | 'a125': 788 4185 | 'a126': 788 4186 | 'a127': 788 4187 | 'a128': 788 4188 | 'a129': 788 4189 | 'a130': 788 4190 | 'a131': 788 4191 | 'a132': 788 4192 | 'a133': 788 4193 | 'a134': 788 4194 | 'a135': 788 4195 | 'a136': 788 4196 | 'a137': 788 4197 | 'a138': 788 4198 | 'a139': 788 4199 | 'a140': 788 4200 | 'a141': 788 4201 | 'a142': 788 4202 | 'a143': 788 4203 | 'a144': 788 4204 | 'a145': 788 4205 | 'a146': 788 4206 | 'a147': 788 4207 | 'a148': 788 4208 | 'a149': 788 4209 | 'a150': 788 4210 | 'a151': 788 4211 | 'a152': 788 4212 | 'a153': 788 4213 | 'a154': 788 4214 | 'a155': 788 4215 | 'a156': 788 4216 | 'a157': 788 4217 | 'a158': 788 4218 | 'a159': 788 4219 | 'a160': 894 4220 | 'a161': 838 4221 | 'a163': 1016 4222 | 'a164': 458 4223 | 'a196': 748 4224 | 'a165': 924 4225 | 'a192': 748 4226 | 'a166': 918 4227 | 'a167': 927 4228 | 'a168': 928 4229 | 'a169': 928 4230 | 'a170': 834 4231 | 'a171': 873 4232 | 'a172': 828 4233 | 'a173': 924 4234 | 'a162': 924 4235 | 'a174': 917 4236 | 'a175': 930 4237 | 'a176': 931 4238 | 'a177': 463 4239 | 'a178': 883 4240 | 'a179': 836 4241 | 'a193': 836 4242 | 'a180': 867 4243 | 'a199': 867 4244 | 'a181': 696 4245 | 'a200': 696 4246 | 'a182': 874 4247 | 'a201': 874 4248 | 'a183': 760 4249 | 'a184': 946 4250 | 'a197': 771 4251 | 'a185': 865 4252 | 'a194': 771 4253 | 'a198': 888 4254 | 'a186': 967 4255 | 'a195': 888 4256 | 'a187': 831 4257 | 'a188': 873 4258 | 'a189': 927 4259 | 'a190': 970 4260 | 'a191': 918 4261 | } 4262 | } 4263 | --------------------------------------------------------------------------------