├── LICENSE ├── Makefile ├── README ├── doc ├── .gitignore └── overview.edoc ├── ebin └── .gitignore ├── erlguten ├── priv ├── font_locations ├── fonts │ ├── README.txt │ ├── adobe │ │ ├── courier │ │ │ ├── pcrb8a.afm │ │ │ ├── pcrbo8a.afm │ │ │ ├── pcrr8a.afm │ │ │ └── pcrro8a.afm │ │ ├── helvetic │ │ │ ├── phvb8a.afm │ │ │ ├── phvbo8a.afm │ │ │ ├── phvr8a.afm │ │ │ └── phvro8a.afm │ │ ├── symbol │ │ │ └── psyr.afm │ │ ├── times │ │ │ ├── ptmb8a.afm │ │ │ ├── ptmbi8a.afm │ │ │ ├── ptmr8a.afm │ │ │ └── ptmri8a.afm │ │ └── zapfding │ │ │ └── pzdr.afm │ ├── gocm │ │ ├── GOCM____.TXT │ │ ├── ase │ │ │ ├── GOCMASE_.CFG │ │ │ ├── GOCMASE_.INF │ │ │ ├── GOCMASE_.afm │ │ │ ├── GOCMASE_.pfb │ │ │ └── GOCMASE_.pfm │ │ └── seq │ │ │ ├── GOCMSEQ_.CFG │ │ │ ├── GOCMSEQ_.INF │ │ │ ├── GOCMSEQ_.afm │ │ │ ├── GOCMSEQ_.pfb │ │ │ └── GOCMSEQ_.pfm │ ├── ocr │ │ ├── file_id.diz │ │ ├── ocra_num.ttf │ │ ├── ocrad___.afm │ │ ├── ocrad___.pfb │ │ ├── ocrad___.pfm │ │ ├── ocrb_num.ttf │ │ ├── ocrbd___.afm │ │ ├── ocrbd___.pfb │ │ ├── ocrbd___.pfm │ │ └── readme.txt │ └── vsecret │ │ ├── README │ │ ├── V-SECRET.BMA │ │ ├── V-SECRET.afm │ │ └── V-SECRET.pfb ├── hyphenation │ ├── README.txt │ ├── dk_merged.tex │ ├── hyph_da_DK.dic │ ├── hyph_en_GB.dic │ ├── hyph_fi_FI.dic │ ├── hyph_nb_NO.dic │ ├── hyph_sv_SE.dic │ ├── original_tex │ │ ├── dkcommon.tex │ │ ├── dkhyph.tex │ │ ├── dkspecial.tex │ │ ├── fihyph.tex │ │ ├── nohyphbx.tex │ │ ├── svhyph.tex │ │ └── ukhyphen.tex │ ├── tb87nemeth.pdf │ └── ukhyphen.tex └── src │ ├── .gitignore │ └── README.txt ├── src ├── eg.hrl ├── eg_afm.erl ├── eg_convert.erl ├── eg_embed.erl ├── eg_font_server.erl ├── eg_hyphenate.erl ├── eg_lib.erl ├── eg_line_break.erl ├── eg_mk_hyphen.erl ├── eg_pdf.erl ├── eg_pdf_analyse.erl ├── eg_pdf_annot.erl ├── eg_pdf_assemble.erl ├── eg_pdf_image.erl ├── eg_pdf_lib.erl ├── eg_pdf_obj.erl ├── eg_pdf_op.erl ├── eg_pdf_page.erl ├── eg_richText.erl ├── eg_richText2pdf.erl ├── eg_xml2richText.erl ├── eg_xml_lite.erl ├── eg_xml_tokenise.erl ├── erlguten.app.src └── erlguten.erl ├── test ├── .gitignore ├── content1.con ├── ebutik.jpg ├── eg_test1.erl ├── eg_test2.erl ├── eg_test3.erl ├── eg_test4.erl ├── eg_test5.erl ├── eg_test6.erl ├── eg_test7.erl ├── erlguten ├── galley_001.gal ├── galley_002.gal ├── joenew.jpg ├── kd_test1.erl ├── template1.tem ├── template_001.tem ├── test1.map ├── test1.pdf ├── test1.xml ├── test2.xml ├── tmo_doc.erl ├── tmo_doc.xml └── tmobile.jpg └── vsn.mk /LICENSE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richcarl/erlguten/debd6120907e5e18b4bca5fbd5bdd9cb653f282d/LICENSE -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include vsn.mk 2 | APPNAME=erlguten 3 | DOC_OPTS={def,{version,\"$(VSN)\"}} 4 | ERLGUTEN=./erlguten 5 | ERL=erl 6 | ERLC=erlc 7 | ERLC_FLAGS=+debug_info +nowarn_unused_vars 8 | PERL=perl 9 | APPSCRIPT = '$$vsn=shift; $$mods=""; while(@ARGV){ $$_=shift; s/^([A-Z].*)$$/\'\''$$1\'\''/; $$mods.=", " if $$mods; $$mods .= $$_; } while(<>) { s/%VSN%/$$vsn/; s/"%MODULES%"/$$mods/; print; }' 10 | 11 | HYPHEN_DICTS = $(wildcard priv/hyphenation/hyph_*.dic) 12 | HYPHEN_SOURCES = $(HYPHEN_DICTS:priv/hyphenation/hyph_%.dic=priv/src/eg_hyphen_rules_%.erl) 13 | .PRECIOUS: $(HYPHEN_SOURCES) 14 | HYPHEN_OBJECTS = $(HYPHEN_SOURCES:priv/src/%.erl=ebin/%.beam) 15 | 16 | ERL_SOURCES = $(wildcard src/*.erl) 17 | MODULES = $(ERL_SOURCES:src/%.erl=%) 18 | ERL_OBJECTS = $(ERL_SOURCES:src/%.erl=ebin/%.beam) \ 19 | ebin/eg_font_map.beam $(HYPHEN_OBJECTS) 20 | 21 | TEST_SOURCES = $(wildcard test/*.erl) 22 | TEST_PDFS = $(TEST_SOURCES:test/%.erl=test/%.pdf) 23 | TEST_OBJECTS = $(TEST_SOURCES:test/%.erl=test/%.beam) 24 | .PRECIOUS: $(TEST_OBJECTS) 25 | 26 | .PHONY: all test doc 27 | 28 | all: $(ERL_OBJECTS) app 29 | 30 | test: all $(TEST_PDFS) 31 | 32 | docs: doc 33 | doc: doc/index.html 34 | 35 | app: ebin/$(APPNAME).app 36 | 37 | clean: 38 | -rm -f $(ERL_OBJECTS) ebin/$(APPNAME).app 39 | -cd priv/src && $(MAKE) clean 40 | -rm -f $(TEST_OBJECTS) $(TEST_PDFS) 41 | -rm -f doc/*.html doc/stylesheet.css doc/erlang.png doc/edoc-info 42 | 43 | 44 | doc/index.html: doc/overview.edoc $(ERL_SOURCES) 45 | $(ERL) -noshell -eval "edoc:application($(APPNAME), \".\", [$(DOC_OPTS)])" -s init stop 46 | 47 | 48 | ebin/%.app: src/%.app.src vsn.mk 49 | $(PERL) -e $(APPSCRIPT) "$(VSN)" $(MODULES) < $< > $@ 50 | 51 | ebin/%.beam: src/%.erl 52 | $(ERLC) $(ERLC_FLAGS) -o ebin $< 53 | 54 | 55 | # eg_hyphen_rules_*.erl files are found in priv/src/, not src/ 56 | ebin/eg_hyphen_rules_%.beam: priv/src/eg_hyphen_rules_%.erl 57 | $(ERLC) $(ERLC_FLAGS) -o ebin $< 58 | 59 | # generate hyphenation modules from .dic files 60 | priv/src/eg_hyphen_rules_%.erl: priv/hyphenation/hyph_%.dic \ 61 | ebin/eg_mk_hyphen.beam ebin/eg_lib.beam 62 | $(ERL) -noshell -pa ebin -s eg_mk_hyphen start $* -s init stop 63 | 64 | 65 | # the font mapping is treated specially with its own Makefile, since we 66 | # can't know here how many font source files will be generated by eg_afm 67 | ebin/eg_font_map.beam: priv/src/Makefile 68 | -cd priv/src && $(MAKE) 69 | 70 | priv/src/Makefile: ebin/eg_afm.beam ebin/eg_lib.beam ebin/eg_pdf_op.beam \ 71 | priv/font_locations 72 | $(ERL) -noshell -pa ebin -s eg_afm make -s init stop 73 | 74 | 75 | test1.pdf: test1.map galley_001.gal content1.con 76 | $(ERLGUTEN) test1.map 77 | 78 | test2.pdf: test2.xml galley_002.gal 79 | $(ERLGUTEN) test2.xml 80 | 81 | tmo_doc.pdf: tmo_doc.xml 82 | 83 | test/%.pdf: test/%.beam $(ERL_OBJECTS) 84 | cd test && erl -pa . -pa ../ebin -noshell -s $* test -s init stop 85 | 86 | test/%.beam: test/%.erl 87 | $(ERLC) $(ERLC_FLAGS) -o test $< 88 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | 1) Install with make 2 | 3 | 2) Try the command 4 | 5 | make test 6 | 7 | This compiles test/eg_test1.erl, ... test/eg_test7.erl and runs them to 8 | generate test/eg_test1.pdf, ... test/eg_test7.pdf. See these modules 9 | for examples of how to use erlguten from an Erlang program. 10 | 11 | Note: The main module 'erlguten.erl' was intended as a batch interface, 12 | using xml files as input (see test/test1.xml), but it never reached a stable 13 | state and is not fit for use. The shell wrapper script 'erlguten' expects to 14 | use this interface, but doesn't in fact work. Contributions are welcome. 15 | -------------------------------------------------------------------------------- /doc/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | edoc-info 3 | erlang.png 4 | stylesheet.css 5 | -------------------------------------------------------------------------------- /doc/overview.edoc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richcarl/erlguten/debd6120907e5e18b4bca5fbd5bdd9cb653f282d/doc/overview.edoc -------------------------------------------------------------------------------- /ebin/.gitignore: -------------------------------------------------------------------------------- 1 | *.beam 2 | erlguten.app 3 | -------------------------------------------------------------------------------- /erlguten: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | erl -pa ./ebin -s erlguten batch $1 -s init stop 3 | 4 | -------------------------------------------------------------------------------- /priv/font_locations: -------------------------------------------------------------------------------- 1 | priv/fonts/adobe/courier 2 | priv/fonts/adobe/helvetic 3 | priv/fonts/adobe/symbol 4 | priv/fonts/adobe/times 5 | priv/fonts/adobe/zapfding 6 | priv/fonts/vsecret 7 | priv/fonts/ocr 8 | priv/fonts/gocm/ase 9 | -------------------------------------------------------------------------------- /priv/fonts/README.txt: -------------------------------------------------------------------------------- 1 | The Adobe AFM files for the base PDF fonts were copied from the TeX Live 2 | distribution. They may be freely distributed. 3 | 4 | The fonts OCR-A-Digits & OCR-B-Digits and Victorias-Secret are only included 5 | here for demo purposes, and for use by Erlguten's test suite. Please refer 6 | to their individual README-files for details on conditions of use. 7 | -------------------------------------------------------------------------------- /priv/fonts/adobe/symbol/psyr.afm: -------------------------------------------------------------------------------- 1 | StartFontMetrics 2.0 2 | Comment Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All rights reserved. 3 | Comment Creation Date: Wed Jan 17 21:48:26 1990 4 | Comment UniqueID 27004 5 | Comment VMusage 28489 37622 6 | FontName Symbol 7 | FullName Symbol 8 | FamilyName Symbol 9 | Weight Medium 10 | ItalicAngle 0 11 | IsFixedPitch false 12 | FontBBox -180 -293 1090 1010 13 | UnderlinePosition -98 14 | UnderlineThickness 54 15 | Version 001.007 16 | Notice Copyright (c) 1985, 1987, 1989, 1990 Adobe Systems Incorporated. All rights reserved. 17 | EncodingScheme FontSpecific 18 | StartCharMetrics 189 19 | C 32 ; WX 250 ; N space ; B 0 0 0 0 ; 20 | C 33 ; WX 333 ; N exclam ; B 128 -17 240 672 ; 21 | C 34 ; WX 713 ; N universal ; B 31 0 681 705 ; 22 | C 35 ; WX 500 ; N numbersign ; B 20 -16 481 673 ; 23 | C 36 ; WX 549 ; N existential ; B 25 0 478 707 ; 24 | C 37 ; WX 833 ; N percent ; B 63 -36 771 655 ; 25 | C 38 ; WX 778 ; N ampersand ; B 41 -18 750 661 ; 26 | C 39 ; WX 439 ; N suchthat ; B 48 -17 414 500 ; 27 | C 40 ; WX 333 ; N parenleft ; B 53 -191 300 673 ; 28 | C 41 ; WX 333 ; N parenright ; B 30 -191 277 673 ; 29 | C 42 ; WX 500 ; N asteriskmath ; B 65 134 427 551 ; 30 | C 43 ; WX 549 ; N plus ; B 10 0 539 533 ; 31 | C 44 ; WX 250 ; N comma ; B 56 -152 194 104 ; 32 | C 45 ; WX 549 ; N minus ; B 11 233 535 288 ; 33 | C 46 ; WX 250 ; N period ; B 69 -17 181 95 ; 34 | C 47 ; WX 278 ; N slash ; B 0 -18 254 646 ; 35 | C 48 ; WX 500 ; N zero ; B 23 -17 471 685 ; 36 | C 49 ; WX 500 ; N one ; B 117 0 390 673 ; 37 | C 50 ; WX 500 ; N two ; B 25 0 475 686 ; 38 | C 51 ; WX 500 ; N three ; B 39 -17 435 685 ; 39 | C 52 ; WX 500 ; N four ; B 16 0 469 685 ; 40 | C 53 ; WX 500 ; N five ; B 29 -17 443 685 ; 41 | C 54 ; WX 500 ; N six ; B 36 -17 467 685 ; 42 | C 55 ; WX 500 ; N seven ; B 24 -16 448 673 ; 43 | C 56 ; WX 500 ; N eight ; B 54 -18 440 685 ; 44 | C 57 ; WX 500 ; N nine ; B 31 -18 460 685 ; 45 | C 58 ; WX 278 ; N colon ; B 81 -17 193 460 ; 46 | C 59 ; WX 278 ; N semicolon ; B 83 -152 221 460 ; 47 | C 60 ; WX 549 ; N less ; B 26 0 523 522 ; 48 | C 61 ; WX 549 ; N equal ; B 11 141 537 390 ; 49 | C 62 ; WX 549 ; N greater ; B 26 0 523 522 ; 50 | C 63 ; WX 444 ; N question ; B 70 -17 412 686 ; 51 | C 64 ; WX 549 ; N congruent ; B 11 0 537 475 ; 52 | C 65 ; WX 722 ; N Alpha ; B 4 0 684 673 ; 53 | C 66 ; WX 667 ; N Beta ; B 29 0 592 673 ; 54 | C 67 ; WX 722 ; N Chi ; B -9 0 704 673 ; 55 | C 68 ; WX 612 ; N Delta ; B 6 0 608 688 ; 56 | C 69 ; WX 611 ; N Epsilon ; B 32 0 617 673 ; 57 | C 70 ; WX 763 ; N Phi ; B 26 0 741 673 ; 58 | C 71 ; WX 603 ; N Gamma ; B 24 0 609 673 ; 59 | C 72 ; WX 722 ; N Eta ; B 39 0 729 673 ; 60 | C 73 ; WX 333 ; N Iota ; B 32 0 316 673 ; 61 | C 74 ; WX 631 ; N theta1 ; B 18 -18 623 689 ; 62 | C 75 ; WX 722 ; N Kappa ; B 35 0 722 673 ; 63 | C 76 ; WX 686 ; N Lambda ; B 6 0 680 688 ; 64 | C 77 ; WX 889 ; N Mu ; B 28 0 887 673 ; 65 | C 78 ; WX 722 ; N Nu ; B 29 -8 720 673 ; 66 | C 79 ; WX 722 ; N Omicron ; B 41 -17 715 685 ; 67 | C 80 ; WX 768 ; N Pi ; B 25 0 745 673 ; 68 | C 81 ; WX 741 ; N Theta ; B 41 -17 715 685 ; 69 | C 82 ; WX 556 ; N Rho ; B 28 0 563 673 ; 70 | C 83 ; WX 592 ; N Sigma ; B 5 0 589 673 ; 71 | C 84 ; WX 611 ; N Tau ; B 33 0 607 673 ; 72 | C 85 ; WX 690 ; N Upsilon ; B -8 0 694 673 ; 73 | C 86 ; WX 439 ; N sigma1 ; B 40 -233 436 500 ; 74 | C 87 ; WX 768 ; N Omega ; B 34 0 736 688 ; 75 | C 88 ; WX 645 ; N Xi ; B 40 0 599 673 ; 76 | C 89 ; WX 795 ; N Psi ; B 15 0 781 684 ; 77 | C 90 ; WX 611 ; N Zeta ; B 44 0 636 673 ; 78 | C 91 ; WX 333 ; N bracketleft ; B 86 -155 299 674 ; 79 | C 92 ; WX 863 ; N therefore ; B 163 0 701 478 ; 80 | C 93 ; WX 333 ; N bracketright ; B 33 -155 246 674 ; 81 | C 94 ; WX 658 ; N perpendicular ; B 15 0 652 674 ; 82 | C 95 ; WX 500 ; N underscore ; B -2 -252 502 -206 ; 83 | C 96 ; WX 500 ; N radicalex ; B 480 881 1090 917 ; 84 | C 97 ; WX 631 ; N alpha ; B 41 -18 622 500 ; 85 | C 98 ; WX 549 ; N beta ; B 61 -223 515 741 ; 86 | C 99 ; WX 549 ; N chi ; B 12 -231 522 499 ; 87 | C 100 ; WX 494 ; N delta ; B 40 -19 481 740 ; 88 | C 101 ; WX 439 ; N epsilon ; B 22 -19 427 502 ; 89 | C 102 ; WX 521 ; N phi ; B 27 -224 490 671 ; 90 | C 103 ; WX 411 ; N gamma ; B 5 -225 484 499 ; 91 | C 104 ; WX 603 ; N eta ; B 0 -202 527 514 ; 92 | C 105 ; WX 329 ; N iota ; B 0 -17 301 503 ; 93 | C 106 ; WX 603 ; N phi1 ; B 36 -224 587 499 ; 94 | C 107 ; WX 549 ; N kappa ; B 33 0 558 501 ; 95 | C 108 ; WX 549 ; N lambda ; B 24 -17 548 739 ; 96 | C 109 ; WX 576 ; N mu ; B 33 -223 567 500 ; 97 | C 110 ; WX 521 ; N nu ; B -9 -16 475 507 ; 98 | C 111 ; WX 549 ; N omicron ; B 35 -19 501 499 ; 99 | C 112 ; WX 549 ; N pi ; B 10 -19 530 487 ; 100 | C 113 ; WX 521 ; N theta ; B 43 -17 485 690 ; 101 | C 114 ; WX 549 ; N rho ; B 50 -230 490 499 ; 102 | C 115 ; WX 603 ; N sigma ; B 30 -21 588 500 ; 103 | C 116 ; WX 439 ; N tau ; B 10 -19 418 500 ; 104 | C 117 ; WX 576 ; N upsilon ; B 7 -18 535 507 ; 105 | C 118 ; WX 713 ; N omega1 ; B 12 -18 671 583 ; 106 | C 119 ; WX 686 ; N omega ; B 42 -17 684 500 ; 107 | C 120 ; WX 493 ; N xi ; B 27 -224 469 766 ; 108 | C 121 ; WX 686 ; N psi ; B 12 -228 701 500 ; 109 | C 122 ; WX 494 ; N zeta ; B 60 -225 467 756 ; 110 | C 123 ; WX 480 ; N braceleft ; B 58 -183 397 673 ; 111 | C 124 ; WX 200 ; N bar ; B 65 -177 135 673 ; 112 | C 125 ; WX 480 ; N braceright ; B 79 -183 418 673 ; 113 | C 126 ; WX 549 ; N similar ; B 17 203 529 307 ; 114 | C 161 ; WX 620 ; N Upsilon1 ; B -2 0 610 685 ; 115 | C 162 ; WX 247 ; N minute ; B 27 459 228 735 ; 116 | C 163 ; WX 549 ; N lessequal ; B 29 0 526 639 ; 117 | C 164 ; WX 167 ; N fraction ; B -180 -12 340 677 ; 118 | C 165 ; WX 713 ; N infinity ; B 26 124 688 404 ; 119 | C 166 ; WX 500 ; N florin ; B 2 -193 494 686 ; 120 | C 167 ; WX 753 ; N club ; B 86 -26 660 533 ; 121 | C 168 ; WX 753 ; N diamond ; B 142 -36 600 550 ; 122 | C 169 ; WX 753 ; N heart ; B 117 -33 631 532 ; 123 | C 170 ; WX 753 ; N spade ; B 113 -36 629 548 ; 124 | C 171 ; WX 1042 ; N arrowboth ; B 24 -15 1024 511 ; 125 | C 172 ; WX 987 ; N arrowleft ; B 32 -15 942 511 ; 126 | C 173 ; WX 603 ; N arrowup ; B 45 0 571 910 ; 127 | C 174 ; WX 987 ; N arrowright ; B 49 -15 959 511 ; 128 | C 175 ; WX 603 ; N arrowdown ; B 45 -22 571 888 ; 129 | C 176 ; WX 400 ; N degree ; B 50 385 350 685 ; 130 | C 177 ; WX 549 ; N plusminus ; B 10 0 539 645 ; 131 | C 178 ; WX 411 ; N second ; B 20 459 413 737 ; 132 | C 179 ; WX 549 ; N greaterequal ; B 29 0 526 639 ; 133 | C 180 ; WX 549 ; N multiply ; B 17 8 533 524 ; 134 | C 181 ; WX 713 ; N proportional ; B 27 123 639 404 ; 135 | C 182 ; WX 494 ; N partialdiff ; B 26 -20 462 746 ; 136 | C 183 ; WX 460 ; N bullet ; B 50 113 410 473 ; 137 | C 184 ; WX 549 ; N divide ; B 10 71 536 456 ; 138 | C 185 ; WX 549 ; N notequal ; B 15 -25 540 549 ; 139 | C 186 ; WX 549 ; N equivalence ; B 14 82 538 443 ; 140 | C 187 ; WX 549 ; N approxequal ; B 14 135 527 394 ; 141 | C 188 ; WX 1000 ; N ellipsis ; B 111 -17 889 95 ; 142 | C 189 ; WX 603 ; N arrowvertex ; B 280 -120 336 1010 ; 143 | C 190 ; WX 1000 ; N arrowhorizex ; B -60 220 1050 276 ; 144 | C 191 ; WX 658 ; N carriagereturn ; B 15 -16 602 629 ; 145 | C 192 ; WX 823 ; N aleph ; B 175 -18 661 658 ; 146 | C 193 ; WX 686 ; N Ifraktur ; B 10 -53 578 740 ; 147 | C 194 ; WX 795 ; N Rfraktur ; B 26 -15 759 734 ; 148 | C 195 ; WX 987 ; N weierstrass ; B 159 -211 870 573 ; 149 | C 196 ; WX 768 ; N circlemultiply ; B 43 -17 733 673 ; 150 | C 197 ; WX 768 ; N circleplus ; B 43 -15 733 675 ; 151 | C 198 ; WX 823 ; N emptyset ; B 39 -24 781 719 ; 152 | C 199 ; WX 768 ; N intersection ; B 40 0 732 509 ; 153 | C 200 ; WX 768 ; N union ; B 40 -17 732 492 ; 154 | C 201 ; WX 713 ; N propersuperset ; B 20 0 673 470 ; 155 | C 202 ; WX 713 ; N reflexsuperset ; B 20 -125 673 470 ; 156 | C 203 ; WX 713 ; N notsubset ; B 36 -70 690 540 ; 157 | C 204 ; WX 713 ; N propersubset ; B 37 0 690 470 ; 158 | C 205 ; WX 713 ; N reflexsubset ; B 37 -125 690 470 ; 159 | C 206 ; WX 713 ; N element ; B 45 0 505 468 ; 160 | C 207 ; WX 713 ; N notelement ; B 45 -58 505 555 ; 161 | C 208 ; WX 768 ; N angle ; B 26 0 738 673 ; 162 | C 209 ; WX 713 ; N gradient ; B 36 -19 681 718 ; 163 | C 210 ; WX 790 ; N registerserif ; B 50 -17 740 673 ; 164 | C 211 ; WX 790 ; N copyrightserif ; B 51 -15 741 675 ; 165 | C 212 ; WX 890 ; N trademarkserif ; B 18 293 855 673 ; 166 | C 213 ; WX 823 ; N product ; B 25 -101 803 751 ; 167 | C 214 ; WX 549 ; N radical ; B 10 -38 515 917 ; 168 | C 215 ; WX 250 ; N dotmath ; B 69 210 169 310 ; 169 | C 216 ; WX 713 ; N logicalnot ; B 15 0 680 288 ; 170 | C 217 ; WX 603 ; N logicaland ; B 23 0 583 454 ; 171 | C 218 ; WX 603 ; N logicalor ; B 30 0 578 477 ; 172 | C 219 ; WX 1042 ; N arrowdblboth ; B 27 -20 1023 510 ; 173 | C 220 ; WX 987 ; N arrowdblleft ; B 30 -15 939 513 ; 174 | C 221 ; WX 603 ; N arrowdblup ; B 39 2 567 911 ; 175 | C 222 ; WX 987 ; N arrowdblright ; B 45 -20 954 508 ; 176 | C 223 ; WX 603 ; N arrowdbldown ; B 44 -19 572 890 ; 177 | C 224 ; WX 494 ; N lozenge ; B 18 0 466 745 ; 178 | C 225 ; WX 329 ; N angleleft ; B 25 -198 306 746 ; 179 | C 226 ; WX 790 ; N registersans ; B 50 -20 740 670 ; 180 | C 227 ; WX 790 ; N copyrightsans ; B 49 -15 739 675 ; 181 | C 228 ; WX 786 ; N trademarksans ; B 5 293 725 673 ; 182 | C 229 ; WX 713 ; N summation ; B 14 -108 695 752 ; 183 | C 230 ; WX 384 ; N parenlefttp ; B 40 -293 436 926 ; 184 | C 231 ; WX 384 ; N parenleftex ; B 40 -85 92 925 ; 185 | C 232 ; WX 384 ; N parenleftbt ; B 40 -293 436 926 ; 186 | C 233 ; WX 384 ; N bracketlefttp ; B 0 -80 341 926 ; 187 | C 234 ; WX 384 ; N bracketleftex ; B 0 -79 55 925 ; 188 | C 235 ; WX 384 ; N bracketleftbt ; B 0 -80 340 926 ; 189 | C 236 ; WX 494 ; N bracelefttp ; B 201 -75 439 926 ; 190 | C 237 ; WX 494 ; N braceleftmid ; B 14 -85 255 935 ; 191 | C 238 ; WX 494 ; N braceleftbt ; B 201 -70 439 926 ; 192 | C 239 ; WX 494 ; N braceex ; B 201 -80 255 935 ; 193 | C 241 ; WX 329 ; N angleright ; B 21 -198 302 746 ; 194 | C 242 ; WX 274 ; N integral ; B 2 -107 291 916 ; 195 | C 243 ; WX 686 ; N integraltp ; B 332 -83 715 921 ; 196 | C 244 ; WX 686 ; N integralex ; B 332 -88 415 975 ; 197 | C 245 ; WX 686 ; N integralbt ; B 39 -81 415 921 ; 198 | C 246 ; WX 384 ; N parenrighttp ; B 54 -293 450 926 ; 199 | C 247 ; WX 384 ; N parenrightex ; B 398 -85 450 925 ; 200 | C 248 ; WX 384 ; N parenrightbt ; B 54 -293 450 926 ; 201 | C 249 ; WX 384 ; N bracketrighttp ; B 22 -80 360 926 ; 202 | C 250 ; WX 384 ; N bracketrightex ; B 305 -79 360 925 ; 203 | C 251 ; WX 384 ; N bracketrightbt ; B 20 -80 360 926 ; 204 | C 252 ; WX 494 ; N bracerighttp ; B 17 -75 255 926 ; 205 | C 253 ; WX 494 ; N bracerightmid ; B 201 -85 442 935 ; 206 | C 254 ; WX 494 ; N bracerightbt ; B 17 -70 255 926 ; 207 | C -1 ; WX 790 ; N apple ; B 56 -3 733 808 ; 208 | EndCharMetrics 209 | EndFontMetrics 210 | -------------------------------------------------------------------------------- /priv/fonts/adobe/zapfding/pzdr.afm: -------------------------------------------------------------------------------- 1 | StartFontMetrics 2.0 2 | Comment Copyright (c) 1985, 1987, 1988, 1989 Adobe Systems Incorporated. All rights reserved. 3 | Comment Creation Date: Fri Dec 1 12:57:42 1989 4 | Comment UniqueID 26200 5 | Comment VMusage 39281 49041 6 | FontName ZapfDingbats 7 | FullName ITC Zapf Dingbats 8 | FamilyName ITC Zapf Dingbats 9 | Weight Medium 10 | ItalicAngle 0 11 | IsFixedPitch false 12 | FontBBox -1 -143 981 820 13 | UnderlinePosition -98 14 | UnderlineThickness 54 15 | Version 001.004 16 | Notice Copyright (c) 1985, 1987, 1988, 1989 Adobe Systems Incorporated. All rights reserved.ITC Zapf Dingbats is a registered trademark of International Typeface Corporation. 17 | EncodingScheme FontSpecific 18 | StartCharMetrics 202 19 | C 32 ; WX 278 ; N space ; B 0 0 0 0 ; 20 | C 33 ; WX 974 ; N a1 ; B 35 72 939 621 ; 21 | C 34 ; WX 961 ; N a2 ; B 35 81 927 611 ; 22 | C 35 ; WX 974 ; N a202 ; B 35 72 939 621 ; 23 | C 36 ; WX 980 ; N a3 ; B 35 0 945 692 ; 24 | C 37 ; WX 719 ; N a4 ; B 34 139 685 566 ; 25 | C 38 ; WX 789 ; N a5 ; B 35 -14 755 705 ; 26 | C 39 ; WX 790 ; N a119 ; B 35 -14 755 705 ; 27 | C 40 ; WX 791 ; N a118 ; B 35 -13 761 705 ; 28 | C 41 ; WX 690 ; N a117 ; B 35 138 655 553 ; 29 | C 42 ; WX 960 ; N a11 ; B 35 123 925 568 ; 30 | C 43 ; WX 939 ; N a12 ; B 35 134 904 559 ; 31 | C 44 ; WX 549 ; N a13 ; B 29 -11 516 705 ; 32 | C 45 ; WX 855 ; N a14 ; B 34 59 820 632 ; 33 | C 46 ; WX 911 ; N a15 ; B 35 50 876 642 ; 34 | C 47 ; WX 933 ; N a16 ; B 35 139 899 550 ; 35 | C 48 ; WX 911 ; N a105 ; B 35 50 876 642 ; 36 | C 49 ; WX 945 ; N a17 ; B 35 139 909 553 ; 37 | C 50 ; WX 974 ; N a18 ; B 35 104 938 587 ; 38 | C 51 ; WX 755 ; N a19 ; B 34 -13 721 705 ; 39 | C 52 ; WX 846 ; N a20 ; B 36 -14 811 705 ; 40 | C 53 ; WX 762 ; N a21 ; B 35 0 727 692 ; 41 | C 54 ; WX 761 ; N a22 ; B 35 0 727 692 ; 42 | C 55 ; WX 571 ; N a23 ; B -1 -68 571 661 ; 43 | C 56 ; WX 677 ; N a24 ; B 36 -13 642 705 ; 44 | C 57 ; WX 763 ; N a25 ; B 35 0 728 692 ; 45 | C 58 ; WX 760 ; N a26 ; B 35 0 726 692 ; 46 | C 59 ; WX 759 ; N a27 ; B 35 0 725 692 ; 47 | C 60 ; WX 754 ; N a28 ; B 35 0 720 692 ; 48 | C 61 ; WX 494 ; N a6 ; B 35 0 460 692 ; 49 | C 62 ; WX 552 ; N a7 ; B 35 0 517 692 ; 50 | C 63 ; WX 537 ; N a8 ; B 35 0 503 692 ; 51 | C 64 ; WX 577 ; N a9 ; B 35 96 542 596 ; 52 | C 65 ; WX 692 ; N a10 ; B 35 -14 657 705 ; 53 | C 66 ; WX 786 ; N a29 ; B 35 -14 751 705 ; 54 | C 67 ; WX 788 ; N a30 ; B 35 -14 752 705 ; 55 | C 68 ; WX 788 ; N a31 ; B 35 -14 753 705 ; 56 | C 69 ; WX 790 ; N a32 ; B 35 -14 756 705 ; 57 | C 70 ; WX 793 ; N a33 ; B 35 -13 759 705 ; 58 | C 71 ; WX 794 ; N a34 ; B 35 -13 759 705 ; 59 | C 72 ; WX 816 ; N a35 ; B 35 -14 782 705 ; 60 | C 73 ; WX 823 ; N a36 ; B 35 -14 787 705 ; 61 | C 74 ; WX 789 ; N a37 ; B 35 -14 754 705 ; 62 | C 75 ; WX 841 ; N a38 ; B 35 -14 807 705 ; 63 | C 76 ; WX 823 ; N a39 ; B 35 -14 789 705 ; 64 | C 77 ; WX 833 ; N a40 ; B 35 -14 798 705 ; 65 | C 78 ; WX 816 ; N a41 ; B 35 -13 782 705 ; 66 | C 79 ; WX 831 ; N a42 ; B 35 -14 796 705 ; 67 | C 80 ; WX 923 ; N a43 ; B 35 -14 888 705 ; 68 | C 81 ; WX 744 ; N a44 ; B 35 0 710 692 ; 69 | C 82 ; WX 723 ; N a45 ; B 35 0 688 692 ; 70 | C 83 ; WX 749 ; N a46 ; B 35 0 714 692 ; 71 | C 84 ; WX 790 ; N a47 ; B 34 -14 756 705 ; 72 | C 85 ; WX 792 ; N a48 ; B 35 -14 758 705 ; 73 | C 86 ; WX 695 ; N a49 ; B 35 -14 661 706 ; 74 | C 87 ; WX 776 ; N a50 ; B 35 -6 741 699 ; 75 | C 88 ; WX 768 ; N a51 ; B 35 -7 734 699 ; 76 | C 89 ; WX 792 ; N a52 ; B 35 -14 757 705 ; 77 | C 90 ; WX 759 ; N a53 ; B 35 0 725 692 ; 78 | C 91 ; WX 707 ; N a54 ; B 35 -13 672 704 ; 79 | C 92 ; WX 708 ; N a55 ; B 35 -14 672 705 ; 80 | C 93 ; WX 682 ; N a56 ; B 35 -14 647 705 ; 81 | C 94 ; WX 701 ; N a57 ; B 35 -14 666 705 ; 82 | C 95 ; WX 826 ; N a58 ; B 35 -14 791 705 ; 83 | C 96 ; WX 815 ; N a59 ; B 35 -14 780 705 ; 84 | C 97 ; WX 789 ; N a60 ; B 35 -14 754 705 ; 85 | C 98 ; WX 789 ; N a61 ; B 35 -14 754 705 ; 86 | C 99 ; WX 707 ; N a62 ; B 34 -14 673 705 ; 87 | C 100 ; WX 687 ; N a63 ; B 36 0 651 692 ; 88 | C 101 ; WX 696 ; N a64 ; B 35 0 661 691 ; 89 | C 102 ; WX 689 ; N a65 ; B 35 0 655 692 ; 90 | C 103 ; WX 786 ; N a66 ; B 34 -14 751 705 ; 91 | C 104 ; WX 787 ; N a67 ; B 35 -14 752 705 ; 92 | C 105 ; WX 713 ; N a68 ; B 35 -14 678 705 ; 93 | C 106 ; WX 791 ; N a69 ; B 35 -14 756 705 ; 94 | C 107 ; WX 785 ; N a70 ; B 36 -14 751 705 ; 95 | C 108 ; WX 791 ; N a71 ; B 35 -14 757 705 ; 96 | C 109 ; WX 873 ; N a72 ; B 35 -14 838 705 ; 97 | C 110 ; WX 761 ; N a73 ; B 35 0 726 692 ; 98 | C 111 ; WX 762 ; N a74 ; B 35 0 727 692 ; 99 | C 112 ; WX 762 ; N a203 ; B 35 0 727 692 ; 100 | C 113 ; WX 759 ; N a75 ; B 35 0 725 692 ; 101 | C 114 ; WX 759 ; N a204 ; B 35 0 725 692 ; 102 | C 115 ; WX 892 ; N a76 ; B 35 0 858 705 ; 103 | C 116 ; WX 892 ; N a77 ; B 35 -14 858 692 ; 104 | C 117 ; WX 788 ; N a78 ; B 35 -14 754 705 ; 105 | C 118 ; WX 784 ; N a79 ; B 35 -14 749 705 ; 106 | C 119 ; WX 438 ; N a81 ; B 35 -14 403 705 ; 107 | C 120 ; WX 138 ; N a82 ; B 35 0 104 692 ; 108 | C 121 ; WX 277 ; N a83 ; B 35 0 242 692 ; 109 | C 122 ; WX 415 ; N a84 ; B 35 0 380 692 ; 110 | C 123 ; WX 392 ; N a97 ; B 35 263 357 705 ; 111 | C 124 ; WX 392 ; N a98 ; B 34 263 357 705 ; 112 | C 125 ; WX 668 ; N a99 ; B 35 263 633 705 ; 113 | C 126 ; WX 668 ; N a100 ; B 36 263 634 705 ; 114 | C 161 ; WX 732 ; N a101 ; B 35 -143 697 806 ; 115 | C 162 ; WX 544 ; N a102 ; B 56 -14 488 706 ; 116 | C 163 ; WX 544 ; N a103 ; B 34 -14 508 705 ; 117 | C 164 ; WX 910 ; N a104 ; B 35 40 875 651 ; 118 | C 165 ; WX 667 ; N a106 ; B 35 -14 633 705 ; 119 | C 166 ; WX 760 ; N a107 ; B 35 -14 726 705 ; 120 | C 167 ; WX 760 ; N a108 ; B 0 121 758 569 ; 121 | C 168 ; WX 776 ; N a112 ; B 35 0 741 705 ; 122 | C 169 ; WX 595 ; N a111 ; B 34 -14 560 705 ; 123 | C 170 ; WX 694 ; N a110 ; B 35 -14 659 705 ; 124 | C 171 ; WX 626 ; N a109 ; B 34 0 591 705 ; 125 | C 172 ; WX 788 ; N a120 ; B 35 -14 754 705 ; 126 | C 173 ; WX 788 ; N a121 ; B 35 -14 754 705 ; 127 | C 174 ; WX 788 ; N a122 ; B 35 -14 754 705 ; 128 | C 175 ; WX 788 ; N a123 ; B 35 -14 754 705 ; 129 | C 176 ; WX 788 ; N a124 ; B 35 -14 754 705 ; 130 | C 177 ; WX 788 ; N a125 ; B 35 -14 754 705 ; 131 | C 178 ; WX 788 ; N a126 ; B 35 -14 754 705 ; 132 | C 179 ; WX 788 ; N a127 ; B 35 -14 754 705 ; 133 | C 180 ; WX 788 ; N a128 ; B 35 -14 754 705 ; 134 | C 181 ; WX 788 ; N a129 ; B 35 -14 754 705 ; 135 | C 182 ; WX 788 ; N a130 ; B 35 -14 754 705 ; 136 | C 183 ; WX 788 ; N a131 ; B 35 -14 754 705 ; 137 | C 184 ; WX 788 ; N a132 ; B 35 -14 754 705 ; 138 | C 185 ; WX 788 ; N a133 ; B 35 -14 754 705 ; 139 | C 186 ; WX 788 ; N a134 ; B 35 -14 754 705 ; 140 | C 187 ; WX 788 ; N a135 ; B 35 -14 754 705 ; 141 | C 188 ; WX 788 ; N a136 ; B 35 -14 754 705 ; 142 | C 189 ; WX 788 ; N a137 ; B 35 -14 754 705 ; 143 | C 190 ; WX 788 ; N a138 ; B 35 -14 754 705 ; 144 | C 191 ; WX 788 ; N a139 ; B 35 -14 754 705 ; 145 | C 192 ; WX 788 ; N a140 ; B 35 -14 754 705 ; 146 | C 193 ; WX 788 ; N a141 ; B 35 -14 754 705 ; 147 | C 194 ; WX 788 ; N a142 ; B 35 -14 754 705 ; 148 | C 195 ; WX 788 ; N a143 ; B 35 -14 754 705 ; 149 | C 196 ; WX 788 ; N a144 ; B 35 -14 754 705 ; 150 | C 197 ; WX 788 ; N a145 ; B 35 -14 754 705 ; 151 | C 198 ; WX 788 ; N a146 ; B 35 -14 754 705 ; 152 | C 199 ; WX 788 ; N a147 ; B 35 -14 754 705 ; 153 | C 200 ; WX 788 ; N a148 ; B 35 -14 754 705 ; 154 | C 201 ; WX 788 ; N a149 ; B 35 -14 754 705 ; 155 | C 202 ; WX 788 ; N a150 ; B 35 -14 754 705 ; 156 | C 203 ; WX 788 ; N a151 ; B 35 -14 754 705 ; 157 | C 204 ; WX 788 ; N a152 ; B 35 -14 754 705 ; 158 | C 205 ; WX 788 ; N a153 ; B 35 -14 754 705 ; 159 | C 206 ; WX 788 ; N a154 ; B 35 -14 754 705 ; 160 | C 207 ; WX 788 ; N a155 ; B 35 -14 754 705 ; 161 | C 208 ; WX 788 ; N a156 ; B 35 -14 754 705 ; 162 | C 209 ; WX 788 ; N a157 ; B 35 -14 754 705 ; 163 | C 210 ; WX 788 ; N a158 ; B 35 -14 754 705 ; 164 | C 211 ; WX 788 ; N a159 ; B 35 -14 754 705 ; 165 | C 212 ; WX 894 ; N a160 ; B 35 58 860 634 ; 166 | C 213 ; WX 838 ; N a161 ; B 35 152 803 540 ; 167 | C 214 ; WX 1016 ; N a163 ; B 34 152 981 540 ; 168 | C 215 ; WX 458 ; N a164 ; B 35 -127 422 820 ; 169 | C 216 ; WX 748 ; N a196 ; B 35 94 698 597 ; 170 | C 217 ; WX 924 ; N a165 ; B 35 140 890 552 ; 171 | C 218 ; WX 748 ; N a192 ; B 35 94 698 597 ; 172 | C 219 ; WX 918 ; N a166 ; B 35 166 884 526 ; 173 | C 220 ; WX 927 ; N a167 ; B 35 32 892 660 ; 174 | C 221 ; WX 928 ; N a168 ; B 35 129 891 562 ; 175 | C 222 ; WX 928 ; N a169 ; B 35 128 893 563 ; 176 | C 223 ; WX 834 ; N a170 ; B 35 155 799 537 ; 177 | C 224 ; WX 873 ; N a171 ; B 35 93 838 599 ; 178 | C 225 ; WX 828 ; N a172 ; B 35 104 791 588 ; 179 | C 226 ; WX 924 ; N a173 ; B 35 98 889 594 ; 180 | C 227 ; WX 924 ; N a162 ; B 35 98 889 594 ; 181 | C 228 ; WX 917 ; N a174 ; B 35 0 882 692 ; 182 | C 229 ; WX 930 ; N a175 ; B 35 84 896 608 ; 183 | C 230 ; WX 931 ; N a176 ; B 35 84 896 608 ; 184 | C 231 ; WX 463 ; N a177 ; B 35 -99 429 791 ; 185 | C 232 ; WX 883 ; N a178 ; B 35 71 848 623 ; 186 | C 233 ; WX 836 ; N a179 ; B 35 44 802 648 ; 187 | C 234 ; WX 836 ; N a193 ; B 35 44 802 648 ; 188 | C 235 ; WX 867 ; N a180 ; B 35 101 832 591 ; 189 | C 236 ; WX 867 ; N a199 ; B 35 101 832 591 ; 190 | C 237 ; WX 696 ; N a181 ; B 35 44 661 648 ; 191 | C 238 ; WX 696 ; N a200 ; B 35 44 661 648 ; 192 | C 239 ; WX 874 ; N a182 ; B 35 77 840 619 ; 193 | C 241 ; WX 874 ; N a201 ; B 35 73 840 615 ; 194 | C 242 ; WX 760 ; N a183 ; B 35 0 725 692 ; 195 | C 243 ; WX 946 ; N a184 ; B 35 160 911 533 ; 196 | C 244 ; WX 771 ; N a197 ; B 34 37 736 655 ; 197 | C 245 ; WX 865 ; N a185 ; B 35 207 830 481 ; 198 | C 246 ; WX 771 ; N a194 ; B 34 37 736 655 ; 199 | C 247 ; WX 888 ; N a198 ; B 34 -19 853 712 ; 200 | C 248 ; WX 967 ; N a186 ; B 35 124 932 568 ; 201 | C 249 ; WX 888 ; N a195 ; B 34 -19 853 712 ; 202 | C 250 ; WX 831 ; N a187 ; B 35 113 796 579 ; 203 | C 251 ; WX 873 ; N a188 ; B 36 118 838 578 ; 204 | C 252 ; WX 927 ; N a189 ; B 35 150 891 542 ; 205 | C 253 ; WX 970 ; N a190 ; B 35 76 931 616 ; 206 | C 254 ; WX 918 ; N a191 ; B 34 99 884 593 ; 207 | C -1 ; WX 410 ; N a86 ; B 35 0 375 692 ; 208 | C -1 ; WX 509 ; N a85 ; B 35 0 475 692 ; 209 | C -1 ; WX 334 ; N a95 ; B 35 0 299 692 ; 210 | C -1 ; WX 509 ; N a205 ; B 35 0 475 692 ; 211 | C -1 ; WX 390 ; N a89 ; B 35 -14 356 705 ; 212 | C -1 ; WX 234 ; N a87 ; B 35 -14 199 705 ; 213 | C -1 ; WX 276 ; N a91 ; B 35 0 242 692 ; 214 | C -1 ; WX 390 ; N a90 ; B 35 -14 355 705 ; 215 | C -1 ; WX 410 ; N a206 ; B 35 0 375 692 ; 216 | C -1 ; WX 317 ; N a94 ; B 35 0 283 692 ; 217 | C -1 ; WX 317 ; N a93 ; B 35 0 283 692 ; 218 | C -1 ; WX 276 ; N a92 ; B 35 0 242 692 ; 219 | C -1 ; WX 334 ; N a96 ; B 35 0 299 692 ; 220 | C -1 ; WX 234 ; N a88 ; B 35 -14 199 705 ; 221 | EndCharMetrics 222 | EndFontMetrics 223 | -------------------------------------------------------------------------------- /priv/fonts/gocm/GOCM____.TXT: -------------------------------------------------------------------------------- 1 | Installation notes for PC users: 2 | 3 | Don't forget to make the WIN.INI changes required to allow the font to 4 | download to your printer. 5 | 6 | Included in this archive are two versions of GoodCityModern: the first one 7 | uses Adobe Standard Encoding, meaning that many of the odd characters in the 8 | 'upper 128' of the ASCII sequence will appear in the places that PC 9 | keyboarders expect them to be. The second version uses sequential encoding (as 10 | did the original upload of the PC version of GoodCityModern). Sequential 11 | encoding means that a lot more characters will be available for typing from 12 | the PC keyboard, but they will not be in the positions that PC keyboarders 13 | expect them to be. 14 | 15 | You will need to make a choice between the GOCMASE_.* font and the GOCMSEQ_.* 16 | font. The first is Adobe Standard Encoded (characters where PC expects them 17 | but fewer characters available), the second is Sequential Encoded (all the 18 | characters available, but they're in different positions than PCs expect 19 | them). DO NOT INSTALL BOTH VERSIONS AT ONCE OR YOU WILL HAVE PROBLEMS. 20 | 21 | Also, both these versions have been run through the FOGFIX program, to make 22 | sure none of the anomalies that can be caused by having nulls or upper ASCII 23 | in the .PFB file will ever show up. 24 | 25 | 26 | Welcome to GoodCityModern, 27 | 28 | GoodCityModern 1.1 Read Me 29 | 30 | Thank all of you for downloading my font. I never in my wildest dreams would 31 | think so many people wanted this obscure, yet historical, font. To date, well 32 | over 3500 people have retrieved or received GoodCityModern. I have received 33 | letters, cards, and phone calls from all over the world. I am pleased that you 34 | are pleased. 35 | 36 | Ah, like software development, there is always the needed 1.1 after the first 37 | version ships. Quite a lot has happened to GCM (GoodCityModern): lots of 38 | tweaking of path points; corrected accented characters; improved some 39 | character widths; a few new characters added. Enough about the software. 40 | 41 | Since GCM's 1.0 release, I have been busy reading about Latin grammar. I 42 | learned several things. The original kerned pairs that Gutenberg created (po, 43 | pe, pp, etc...) are actually special abbreviations for Latin prefixes, 44 | suffixes, and phrases. He attempted to carry over the medieval scribe's style 45 | of spelling and 'shorthand' for fitting long words at the end of lines. 46 | 47 | Some have complained that the spacing for GCM is uneven or too tight. Go look 48 | at a Gutenberg Bible reproduction and see for yourself. It's far easier to 49 | unkern a few pairs to one's liking than try to know which pairs should be 50 | kerned. Until Apple releases the Line Layout Manager, there is no smart way to 51 | duplicate how Gutenberg typeset his 42 line Bible. 52 | 53 | The main new 'feature' of this version is a set of Lombardic caps to be used 54 | as initial caps with GCM. The original caps will be another font down the way. 55 | 56 | Remember: Blessed is one who teaches another to scribe the Letters. Shalom. 57 | 58 | ------------------------ 59 | GoodCityModern 1.0 Read Me 60 | 61 | Naturally, you may be asking yourself, 'How did he create this font?' It 62 | started late one night, restless... Glancing through some type books, I 63 | stopped upon a reproduction of the Gutenberg typeface; thought, that would be 64 | a great typeface to do in Fontographer. Thinking further, perhaps several 65 | weeks in the library and several more at my Mac with Fontographer 3.0 and I 66 | would be done. Hmm, things do not happen quite that way. Murphy's law was 67 | determined to be bothersome. There were only fragments of pages to go by... My 68 | Mac and scanner broke, requiring costly repairs. However, while visiting my 69 | parents (near C.W. Post Campus of Long Island University in Brookville, New 70 | York), I found a full character set to copy at the college library. Work could 71 | commence. 72 | 73 | The reproduction was small and the copier worse, but I managed. Studying the 74 | page, I quickly realized several key points. One: Gutenberg designed the font 75 | with a deep understanding of Latin grammar and spelling. He had kerning pairs 76 | based on repeated letter usage throughout the Bible and kerned small words 77 | into tight units (i.e., "our," "of," and "and"). Second: That he posited key 78 | geometric guidelines to aid in creating the font. He was doing more than just 79 | capturing the scribe-like strokes of his day, but in addition had a respect 80 | for the metal and ink to be used in printing his Bible. This second point made 81 | it possible to actually create the font; because of the smallness of the 82 | reproduced letters, I had to re-create (for techies, 'reverse-engineer') them. 83 | My loupe and the page became very close friends! 84 | 85 | After some hand drawings to 'feel' the letter forms, I scanned the page at 150 86 | dpi and made a bitmap font using Fontastic Plus(tm). Sometimes, I felt another 87 | hand on my mouse while fat-bitting away late at night... Then, I typed Latin 88 | text into Fontastic Plus' sample text edit window to see the typeface in 89 | actual context. Next, using the bitmap font, a PostScript printer, and Apple's 90 | print driver, I printed a page of characters at 200% enlargement with the 91 | option 'smoothing' (to partly smooth out the jaggies) checked from Apple's 92 | print driver. Thus, one could scale and smooth out a bitmap font -- without 93 | fatbitting a lot. Finally, I cleaned up the scan in MacPaint(R). 94 | 95 | I did preliminary versions in Fontographer 3.0.5 thru 3.1. As the in-house 96 | tester for FreeHand 3.0, I was naturally thinking mostly in terms of FreeHand, 97 | constantly experimenting with new ways to use it. At one point, I wondered 98 | "how much of my font could I do in FreeHand?" So, I placed the scanned image 99 | into FreeHand 3.0 to trace and refine -- using all its new features to 100 | accomplish the task. For example, while Fontographer has layers, I preferred 101 | the naming and ordering of the layers palette that FreeHand offered. Yes, bcp 102 | by bcp, the font came alive. There were days I felt a presence in the room... 103 | 104 | Once the character paths were done, I simply option-copied them from FreeHand 105 | into Fontographer 3.2. Once paths are in Fontographer, one can create the 106 | needed ligatures. Finally, after upgrading from Fontographer 3.2 to 3.3, I 107 | created kerning pairs. Based on the same careful study Gutenberg did on Latin 108 | letters, I needed to create 800 pairs! Now as of 5:15 pm, June 18, 1991, using 109 | Fontographer 3.3, I was finished. 110 | 111 | A few historical notes. The original typeface was created for Latin, not for 112 | modern English; hence, the 'modern' in the name of my translation. I had to 113 | create a full Roman set everyone can use. However, there is an exact Latin 114 | version which is not done. The 'goodcity' part of its name comes from German: 115 | guten-good, burg-city (or fortress), (actually, Earl Allen, a fellow Altsysian 116 | coined the term: Thanks, Earl!) [Earl notes that he goofed on the translation 117 | from German, mistakenly translating 'berg' as if it were spelled 'burg.' A 118 | better translation would be GoodMountModern, since 'berg' should actually be 119 | translated 'Mountain' rather than 'City,' as anyone who knows what an iceberg 120 | is would have done. Apologies for the mistranslation, but Andrew has decided 121 | to stick with the name for now.] 122 | 123 | Enjoy and use in good health. 124 | 125 | Andrew S. Meit Altsys Tester (and Stackhead) Altsys Corporation 126 | 127 | If you have any comments you'd like to pass on to Andrew you can send them to 128 | him at CompuServe: 76004,2071; MCI Mail: ALTSYS; America Online: Altsys; 129 | AppleLink: D0590. If you'd prefer to call, the phone number is (214) 680-2060 130 | and the FAX number is (214) 680-0537. Andrew appreciates any and all feedback 131 | you can give him. 132 | -------------------------------------------------------------------------------- /priv/fonts/gocm/ase/GOCMASE_.CFG: -------------------------------------------------------------------------------- 1 | # Installer Configuration File 2 | # Contains package specific information used by the install program. 3 | # 4 | DISKSIZE 0 5 | FILE INSTALL.CFG 1 6 | FILE README 1 7 | # 8 | # Font entry fields: 9 | # Fontname - PostScript name of the typeface 10 | # Filename - 5 character PC filename base 11 | # Number of the disk this filetype sits on or zero if not present 12 | # One entry for each filetype PFB, INF, CTF, PFM, ABF, AFM 13 | # 14 | FONT GoodCityModern GoCM____ 1 1 0 1 0 1 15 | # 16 | DISKNAME 1 'GoodCityModern' 17 | # 18 | PKGNAME GoodCityModern 19 | -------------------------------------------------------------------------------- /priv/fonts/gocm/ase/GOCMASE_.INF: -------------------------------------------------------------------------------- 1 | FontName (GoodCityModern) 2 | FullName (GoodCityModern) 3 | FamilyName (GoodCityModern) 4 | AppleName (GoodCityModern) 5 | AppleFONDID 1759 6 | version (001.000) 7 | isFixedPitch false 8 | CharacterSet (isoAdobe) 9 | Encoding (AdobeStandardEncoding) 10 | ItalicAngle 0 11 | CapHeight 681 12 | UnderlinePosition -133 13 | UnderlineThickness 20 14 | StemWidth 30 15 | Serif true 16 | Pi false 17 | MSMenuName (GoodCityModern) 18 | VPMenuName (GoodCityModern) 19 | WORDMenuName (GoodCityModern) 20 | VPTypefaceID 4021258 21 | VPStyle (N) 22 | PCFileNamePrefix (GoCM____) 23 | PCLTypefaceID 4021258 24 | PCLStrokeWeight 0 25 | PCLStyle 0 26 | -------------------------------------------------------------------------------- /priv/fonts/gocm/ase/GOCMASE_.pfb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richcarl/erlguten/debd6120907e5e18b4bca5fbd5bdd9cb653f282d/priv/fonts/gocm/ase/GOCMASE_.pfb -------------------------------------------------------------------------------- /priv/fonts/gocm/ase/GOCMASE_.pfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richcarl/erlguten/debd6120907e5e18b4bca5fbd5bdd9cb653f282d/priv/fonts/gocm/ase/GOCMASE_.pfm -------------------------------------------------------------------------------- /priv/fonts/gocm/seq/GOCMSEQ_.CFG: -------------------------------------------------------------------------------- 1 | # Installer Configuration File 2 | # Contains package specific information used by the install program. 3 | # 4 | DISKSIZE 0 5 | FILE INSTALL.CFG 1 6 | FILE README 1 7 | # 8 | # Font entry fields: 9 | # Fontname - PostScript name of the typeface 10 | # Filename - 5 character PC filename base 11 | # Number of the disk this filetype sits on or zero if not present 12 | # One entry for each filetype PFB, INF, CTF, PFM, ABF, AFM 13 | # 14 | FONT GoodCityModern GoCM____ 1 1 0 1 0 1 15 | # 16 | DISKNAME 1 'GoodCityModern' 17 | # 18 | PKGNAME GoodCityModern 19 | -------------------------------------------------------------------------------- /priv/fonts/gocm/seq/GOCMSEQ_.INF: -------------------------------------------------------------------------------- 1 | FontName (GoodCityModern) 2 | FullName (GoodCityModern) 3 | FamilyName (GoodCityModern) 4 | AppleName (GoodCityModern) 5 | AppleFONDID 1759 6 | version (001.000) 7 | isFixedPitch false 8 | CharacterSet (isoAdobe) 9 | Encoding (AppleStandard) 10 | ItalicAngle 0 11 | CapHeight 681 12 | UnderlinePosition -133 13 | UnderlineThickness 20 14 | StemWidth 30 15 | Serif true 16 | Pi false 17 | MSMenuName (GoodCityModern) 18 | VPMenuName (GoodCityModern) 19 | WORDMenuName (GoodCityModern) 20 | VPTypefaceID 4021258 21 | VPStyle (N) 22 | PCFileNamePrefix (GoCM____) 23 | PCLTypefaceID 4021258 24 | PCLStrokeWeight 0 25 | PCLStyle 0 26 | -------------------------------------------------------------------------------- /priv/fonts/gocm/seq/GOCMSEQ_.pfb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richcarl/erlguten/debd6120907e5e18b4bca5fbd5bdd9cb653f282d/priv/fonts/gocm/seq/GOCMSEQ_.pfb -------------------------------------------------------------------------------- /priv/fonts/gocm/seq/GOCMSEQ_.pfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richcarl/erlguten/debd6120907e5e18b4bca5fbd5bdd9cb653f282d/priv/fonts/gocm/seq/GOCMSEQ_.pfm -------------------------------------------------------------------------------- /priv/fonts/ocr/file_id.diz: -------------------------------------------------------------------------------- 1 | OCR-A-Digits and OCR-B-Digits - are two 2 | sample fonts from the author's 3 | OCR-A/OCR-B font package. They include 4 | only the digits 0-9 and the space 5 | character and are useful for inventory, 6 | ID numbers, tracking, etc. 7 | -------------------------------------------------------------------------------- /priv/fonts/ocr/ocra_num.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richcarl/erlguten/debd6120907e5e18b4bca5fbd5bdd9cb653f282d/priv/fonts/ocr/ocra_num.ttf -------------------------------------------------------------------------------- /priv/fonts/ocr/ocrad___.afm: -------------------------------------------------------------------------------- 1 | StartFontMetrics 2.0 2 | Comment Generated by Fontographer 3.5 Mon Oct 04 15:20:45 1993 3 | FontName OCR-A-Digits 4 | FullName OCR-A-Digits 5 | FamilyName OCR-A-Digits 6 | Weight Normal 7 | Notice copyright 1993 Jerry Whiting 8 | ItalicAngle 0 9 | IsFixedPitch false 10 | UnderlinePosition -120 11 | UnderlineThickness 20 12 | Version 1.000 13 | EncodingScheme AdobeStandardEncoding 14 | FontBBox 0 0 523 666 15 | CapHeight 610 16 | XHeight 407 17 | Descender -186 18 | Ascender 814 19 | StartCharMetrics 12 20 | C 32 ; WX 200 ; N space ; B 0 0 0 0 ; 21 | C 48 ; WX 620 ; N zero ; B 98 0 522 666 ; 22 | C 49 ; WX 620 ; N one ; B 98 0 522 666 ; 23 | C 50 ; WX 620 ; N two ; B 97 0 522 666 ; 24 | C 51 ; WX 620 ; N three ; B 96 0 520 666 ; 25 | C 52 ; WX 620 ; N four ; B 141 0 523 666 ; 26 | C 53 ; WX 620 ; N five ; B 97 0 521 665 ; 27 | C 54 ; WX 620 ; N six ; B 97 0 521 666 ; 28 | C 55 ; WX 620 ; N seven ; B 98 0 522 666 ; 29 | C 56 ; WX 620 ; N eight ; B 98 0 522 666 ; 30 | C 57 ; WX 620 ; N nine ; B 97 0 521 666 ; 31 | C -1 ; WX 500 ; N nbspace ; B 0 0 0 0 ; 32 | EndCharMetrics 33 | EndFontMetrics 34 | -------------------------------------------------------------------------------- /priv/fonts/ocr/ocrad___.pfb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richcarl/erlguten/debd6120907e5e18b4bca5fbd5bdd9cb653f282d/priv/fonts/ocr/ocrad___.pfb -------------------------------------------------------------------------------- /priv/fonts/ocr/ocrad___.pfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richcarl/erlguten/debd6120907e5e18b4bca5fbd5bdd9cb653f282d/priv/fonts/ocr/ocrad___.pfm -------------------------------------------------------------------------------- /priv/fonts/ocr/ocrb_num.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richcarl/erlguten/debd6120907e5e18b4bca5fbd5bdd9cb653f282d/priv/fonts/ocr/ocrb_num.ttf -------------------------------------------------------------------------------- /priv/fonts/ocr/ocrbd___.afm: -------------------------------------------------------------------------------- 1 | StartFontMetrics 2.0 2 | Comment Generated by Fontographer 3.5 Mon Oct 04 15:25:50 1993 3 | FontName OCR-B-Digits 4 | FullName OCR-B-Digits 5 | FamilyName OCR-B-Digits 6 | Weight Normal 7 | Notice copyright 1993 Jerry Whiting 8 | ItalicAngle 0 9 | IsFixedPitch false 10 | UnderlinePosition -120 11 | UnderlineThickness 20 12 | Version 1.000 13 | EncodingScheme AdobeStandardEncoding 14 | FontBBox 0 -13 621 742 15 | CapHeight 610 16 | XHeight 407 17 | Descender -186 18 | Ascender 814 19 | StartCharMetrics 12 20 | C 32 ; WX 200 ; N space ; B 0 0 0 0 ; 21 | C 48 ; WX 763 ; N zero ; B 141 -13 620 735 ; 22 | C 49 ; WX 763 ; N one ; B 173 -10 473 734 ; 23 | C 50 ; WX 763 ; N two ; B 160 0 601 738 ; 24 | C 51 ; WX 763 ; N three ; B 143 -10 613 725 ; 25 | C 52 ; WX 763 ; N four ; B 143 -10 620 734 ; 26 | C 53 ; WX 763 ; N five ; B 166 -10 582 725 ; 27 | C 54 ; WX 763 ; N six ; B 141 -13 621 738 ; 28 | C 55 ; WX 763 ; N seven ; B 145 -10 618 725 ; 29 | C 56 ; WX 763 ; N eight ; B 142 -13 621 735 ; 30 | C 57 ; WX 763 ; N nine ; B 141 -9 621 742 ; 31 | C -1 ; WX 500 ; N nbspace ; B 0 0 0 0 ; 32 | EndCharMetrics 33 | EndFontMetrics 34 | -------------------------------------------------------------------------------- /priv/fonts/ocr/ocrbd___.pfb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richcarl/erlguten/debd6120907e5e18b4bca5fbd5bdd9cb653f282d/priv/fonts/ocr/ocrbd___.pfb -------------------------------------------------------------------------------- /priv/fonts/ocr/ocrbd___.pfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richcarl/erlguten/debd6120907e5e18b4bca5fbd5bdd9cb653f282d/priv/fonts/ocr/ocrbd___.pfm -------------------------------------------------------------------------------- /priv/fonts/ocr/readme.txt: -------------------------------------------------------------------------------- 1 | OCR-A-Digits & OCR-B-Digits 2 | copyright 1993 Jerry Whiting 3 | 4 | Azalea Software, Inc. 5 | PO Box 16745 6 | Seattle WA 98116-0745 7 | 8 | 800 48-ASOFT 9 | 206 937.5919 voice/FAX 10 | 11 | These two fonts are samples from our OCR-A/OCR-B font package. OCR-A-Digits 12 | and OCR-B-Digits include only the digits 0-9 and the space character. They 13 | are ideal for inventory, ID numbers, tracking, etc., and they serve as a demo 14 | of our full OCR-A/OCR-B font set which includes the full ASCII character set. 15 | It is only $99 and available directly from us. 16 | 17 | Install the TrueType font using the Control Panel/Fonts from the Program 18 | Manager. Install the Type 1 fonts using the ATM Control Panel. They will then 19 | appear in the font menu of any Windows application. 20 | 21 | Azalea Software, Inc. specializes in quality bar code printing solutions. We 22 | sell fonts and utilities for both Windows and the Macintosh. Code 39, UPC, 23 | EAN, Bookland, Code 93, etc. We love to hack bar codes! 24 | 25 | 26 | All trademarks & registered trademarks are property of their respective owners. 27 | -------------------------------------------------------------------------------- /priv/fonts/vsecret/README: -------------------------------------------------------------------------------- 1 | Victorias-Secret is a Type 1 PostScript font constructed from the 2 | handwriting (printing, mostly) of Victoria Salter, the Office Manager of 3 | the Columbia University Music Department. Note the extra swashes on the 4 | upper-case E. You'll find every character Victoria routinely uses, 5 | including alphabet, numbers and punctuation. 6 | 7 | Victorias-Secret is copyright (c) 1992 by David Rakowski. All Rights 8 | Reserved. It is distributed free of charge, providing the documentation 9 | accompanies any copies sold or traded. Commercial distribution is 10 | prohibited. Nonprofit companies and user groups may freely distribute this 11 | font on their nominal charge disks. 12 | 13 | Victorias-Secret comes from the zero-person type foundry fictitiously known 14 | as Insect Bytes. Although we don't understand it when other people say it, 15 | we also claim that our fonts are "hand crafted." We feel that calling a 16 | digital typeface "hand crafted" is somewhat akin to calling a television 17 | "mother." 18 | -------------------------------------------------------------------------------- /priv/fonts/vsecret/V-SECRET.BMA: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richcarl/erlguten/debd6120907e5e18b4bca5fbd5bdd9cb653f282d/priv/fonts/vsecret/V-SECRET.BMA -------------------------------------------------------------------------------- /priv/fonts/vsecret/V-SECRET.afm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richcarl/erlguten/debd6120907e5e18b4bca5fbd5bdd9cb653f282d/priv/fonts/vsecret/V-SECRET.afm -------------------------------------------------------------------------------- /priv/fonts/vsecret/V-SECRET.pfb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richcarl/erlguten/debd6120907e5e18b4bca5fbd5bdd9cb653f282d/priv/fonts/vsecret/V-SECRET.pfb -------------------------------------------------------------------------------- /priv/hyphenation/README.txt: -------------------------------------------------------------------------------- 1 | __How to add additional Hyphenation rules for new country/language ?__ 2 | 3 | * Get the appropriate .tex file/s from TeX (more info about this below). 4 | 5 | Open Office has similar (.dic) files that may or may not work (and may in 6 | turn be based on .tex files) - the main issue is that they support some 7 | additional hyphenation rules and sometimes have more than one substring rule 8 | with the same text (when the numbers are removed) - which our code doesn't 9 | handle - .tex files don't appear to have this issue. 10 | 11 | * The .dic file to create and the ones that already exist, have a format 12 | inspired by Open Office, as I started out with OO .dic files before I realised 13 | that they didn't quite work. 14 | 15 | The exact format is explained in eg_mk_hyphen:start/2. 16 | 17 | It should be fairly easy to create a erlguten .dic file by removing the .tex 18 | "%" commets, "\xxx" commands, "{", "}" markers, replace all non-ascii 19 | escape codes with proper latin-1 letters and add the encoding header and 20 | possibly a "\hyphenation" block. 21 | 22 | Note: this may later make it simpler to handle OO .dic files fully. 23 | 24 | * The new file should be placed in erlguten/priv/hyphenation. 25 | 26 | * It should be named "hyphen_languagecode_COUNTRYCODE.dic" 27 | e.g. "hyphen_sv_SE.dic" where sv = the Swedish language and 28 | SE = Sweden the country. See iso3166 and iso639 for the codes to use. 29 | 30 | * You might also want to add a entry in eg_hyphenate:test/1 to check how well 31 | the hyphenation rules work for the new language. 32 | 33 | Note: The .dic support is still somewhat hackish (2009-08-07), there are 34 | several things that can be done to improve it. 35 | - Check that erlguten can truely handle the dic files fully, 36 | hyphen_fi_FI.dic has some rules with spaces in them which will probably 37 | not be used as erlguten seams to mostly pass individual words (space 38 | separated char sequences) for hyphenation. 39 | - tb87nemeth.pdf - "Automatic Non-Standard Hyphenation in OpenOffice" 40 | explains a bit about some of the extra features supported by OO .dic 41 | files which aren't supported by erlgutens TeX based solution. 42 | 43 | =============================================================================== 44 | 45 | The .tex files used have been extracted from the Ubuntu texlive-lang-all 46 | package (installed using synaptic). 47 | 48 | The individual .tex files when can be found in: 49 | /usr/share/texmf-texlive/tex/generic/hyphen/ 50 | 51 | Note that some languages use several .tex files. 52 | 53 | ------------------------------------------------------------------------------- 54 | 55 | * hyph_en_GB.dic based on ukhyphen.tex (version 2007.dfsg.4-1), ukhyphen.tex 56 | is the same as the one in ".../erlguten/priv/" 57 | - Hyphenation mostly works. 58 | 59 | * hyph_sv_SE.dic is based on svhyph.tex (version 2007.dfsg.4-1) 60 | - Some hyphenations points end up being wrong, especialy between compound words 61 | and between double consonats. 62 | 63 | * hyph_nb_NO.dic is based on nohyphbx.tex (version 2007.dfsg.4-1) 64 | - Some hyphenations points end up being wrong, especialy between compound words 65 | and between double consonats. 66 | 67 | * hyph_fi_FI.dic is based on fihyph.tex (version 2007.dfsg.4-1) 68 | - Some hyphenations points end up being wrong, especialy between compound words 69 | and between double consonats. 70 | 71 | * hyph_da_DK.dic is based on a merged of dkhyph.tex, dkspecial.tex and 72 | dkcommon.tex (version 2007.dfsg.4-1). 73 | - Some hyphenations points end up being wrong, especialy between compound words 74 | and between double consonats. 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /priv/hyphenation/hyph_da_DK.dic: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richcarl/erlguten/debd6120907e5e18b4bca5fbd5bdd9cb653f282d/priv/hyphenation/hyph_da_DK.dic -------------------------------------------------------------------------------- /priv/hyphenation/hyph_fi_FI.dic: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richcarl/erlguten/debd6120907e5e18b4bca5fbd5bdd9cb653f282d/priv/hyphenation/hyph_fi_FI.dic -------------------------------------------------------------------------------- /priv/hyphenation/hyph_nb_NO.dic: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richcarl/erlguten/debd6120907e5e18b4bca5fbd5bdd9cb653f282d/priv/hyphenation/hyph_nb_NO.dic -------------------------------------------------------------------------------- /priv/hyphenation/hyph_sv_SE.dic: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richcarl/erlguten/debd6120907e5e18b4bca5fbd5bdd9cb653f282d/priv/hyphenation/hyph_sv_SE.dic -------------------------------------------------------------------------------- /priv/hyphenation/original_tex/dkhyph.tex: -------------------------------------------------------------------------------- 1 | %% dkhyphen.tex 2 | %% Copyright 1994 Frank Jensen (Frank.Jensen@hugin.com) 3 | % 4 | % This work may be distributed and/or modified under the 5 | % conditions of the LaTeX Project Public License, either version 1.3 6 | % of this license or (at your option) any later version. 7 | % The latest version of this license is in 8 | % http://www.latex-project.org/lppl.txt 9 | % and version 1.3 or later is part of all distributions of LaTeX 10 | % version 2003/12/01 or later. 11 | % 12 | % This work consists of the files dkcommon.tex, dkhyphen.tex and 13 | % dkspecial.tex. 14 | % 15 | % This file contains the hyphenation patterns for danish. 16 | % 17 | % The danish letters have been encoded as follows: 18 | % \ae -> X 19 | % \o -> Y 20 | % \aa -> Z 21 | % 22 | % 23 | % First, we input the patterns containing only `normal' letters (a-z). 24 | \input dkcommon 25 | 26 | % Next, we input patterns for the extra danish letters. We input them 27 | % twice: once for OT1 encoding and once for T1 encoding. 28 | 29 | % OT1 encoding: we have to specify a value for the \aa letter; it can 30 | % be any value (it will not be used, since OT1 doesn't contain the letter). 31 | \begingroup 32 | \lccode`X="1A 33 | \lccode`Y="1C 34 | \lccode`Z="97 % (almost) arbitrary 35 | \input dkspecial 36 | \endgroup 37 | 38 | % T1 encoding: 39 | \begingroup 40 | \lccode`X=230 41 | \lccode`Y=248 42 | \lccode`Z=229 43 | \input dkspecial 44 | \endgroup 45 | \endinput 46 | -------------------------------------------------------------------------------- /priv/hyphenation/original_tex/dkspecial.tex: -------------------------------------------------------------------------------- 1 | %% dkspecial.tex 2 | %% Copyright 1994 Frank Jensen (Frank.Jensen@hugin.com) 3 | % 4 | % This work may be distributed and/or modified under the 5 | % conditions of the LaTeX Project Public License, either version 1.3 6 | % of this license or (at your option) any later version. 7 | % The latest version of this license is in 8 | % http://www.latex-project.org/lppl.txt 9 | % and version 1.3 or later is part of all distributions of LaTeX 10 | % version 2003/12/01 or later. 11 | % 12 | % This work consists of the files dkcommon.tex, dkhyphen.tex and 13 | % dkspecial.tex. 14 | \patterns{ % just type if you're not using INITEX 15 | .sZ3 16 | .Xr5i 17 | .Yv3r 18 | a3tY 19 | a5vX 20 | brYd3 21 | 5bX 22 | 5drYv 23 | dstZ4 24 | 3dX 25 | 3dY 26 | e3lX 27 | e3lY 28 | e3rY 29 | er5Yn 30 | e5tX 31 | e5tY 32 | e1vX 33 | e3X 34 | e5Z 35 | 3fX 36 | 3fY 37 | fY4r5en 38 | giY4 39 | g4sY 40 | g5sZ 41 | 3gX 42 | 3gY1 43 | 3gZ 44 | i5tX 45 | i3Y 46 | 3kY 47 | 3kZ 48 | lingeniY4 49 | l3vX 50 | 5lYs 51 | m5tZ 52 | 1mX 53 | 3mY 54 | 3mZ 55 | n3kX 56 | n5tX 57 | 3nX 58 | 4n5Xb 59 | 5nY 60 | o5lX 61 | or3Y 62 | o5Z 63 | 5prX 64 | 5pXd 65 | pZ3 66 | r5kX 67 | r5tX 68 | r5tY 69 | r3vX 70 | r5Xl 71 | 4rYn 72 | 5rYr 73 | 3rZd 74 | r5Zr 75 | s4kZ 76 | 3slZ 77 | s4nX 78 | 5stY 79 | 1stZ 80 | 1sX 81 | 4s5Xn 82 | 1sY 83 | s5Yk 84 | sZ4r5 85 | ti4Y 86 | 3trXk. 87 | t4sY 88 | t5sZ 89 | t3vX 90 | u3lX 91 | 3vXrd 92 | 1vXrk 93 | 5vZ 94 | y5vX 95 | Xb3l 96 | X3c 97 | X3e 98 | Xg5a 99 | X4gek 100 | X4g5r 101 | Xgs5 102 | X5i 103 | X5kv 104 | Xlle4 105 | Xn1dr 106 | X5o 107 | X1re 108 | Xr4g5r 109 | X3ri 110 | Xr4ma 111 | Xr4mo 112 | Xr5s 113 | X5si 114 | X3so 115 | X3ste 116 | X3ve 117 | Yde5 118 | Y3e 119 | Y1je 120 | Y3ke 121 | Y3le 122 | Yms5 123 | Yn3st 124 | Yn4t3 125 | Y1re 126 | Y3ri 127 | Yrne3 128 | Yr5o 129 | Y1ve 130 | Z1d 131 | Z1e 132 | Z5h 133 | Z3l 134 | Z3re 135 | Zrs5t 136 | Z5sk 137 | Z3t 138 | } 139 | \endinput 140 | -------------------------------------------------------------------------------- /priv/hyphenation/original_tex/fihyph.tex: -------------------------------------------------------------------------------- 1 | % 2 | % -----> Finnish hyphenation patterns for MLPCTeX <------ 3 | % First release January -86 by Kauko Saarinen, 4 | % Computing Centre, University of Jyvaskyla, Finland 5 | % 6 | % Completely rewritten January -88. The new patterns make 7 | % much less mistakes with foreign and compound words. 8 | % The article "Automatic Hyphenation of Finnish" 9 | % by Professor Fred Karlsson is also referred 10 | % --------------------------------------------------------- 11 | % 12 | % 8th March -89 (vers. 2.2), some vowel triples by Fred Karlsson added. 13 | % 9th January - 95: added \uccode and \lccode by Thomas Esser 14 | % 15 | % ********* Patterns may be freely distributed ********** 16 | % 17 | % 18 | \begingroup 19 | \uccode`^^e4=`^^c4 \uccode`^^f6=`^^d6 20 | \lccode`^^e4=`^^e4 \lccode`^^f6=`^^f6 21 | \patterns{ 22 | 1ba 23 | 1be 24 | 1bi 25 | 1bo 26 | 1bu 27 | 1by 28 | 1da 29 | 1de 30 | 1di 31 | 1do 32 | 1du 33 | 1dy 34 | 1d^^e4 35 | 1d^^f6 36 | 1fa 37 | 1fe 38 | 1fi 39 | 1fo 40 | 1fu 41 | 1fy 42 | 1ga 43 | 1ge 44 | 1gi 45 | 1go 46 | 1gu 47 | 1gy 48 | 1g^^e4 49 | 1g^^f6 50 | 1ha 51 | 1he 52 | 1hi 53 | 1ho 54 | 1hu 55 | 1hy 56 | 1h^^e4 57 | 1h^^f6 58 | 1ja 59 | 1je 60 | 1ji 61 | 1jo 62 | 1ju 63 | 1jy 64 | 1j^^e4 65 | 1j^^f6 66 | 1ka 67 | 1ke 68 | 1ki 69 | 1ko 70 | 1ku 71 | 1ky 72 | 1k^^e4 73 | 1k^^f6 74 | 1la 75 | 1le 76 | 1li 77 | 1lo 78 | 1lu 79 | 1ly 80 | 1l^^e4 81 | 1l^^f6 82 | 1ma 83 | 1me 84 | 1mi 85 | 1mo 86 | 1mu 87 | 1my 88 | 1m^^e4 89 | 1m^^f6 90 | 1na 91 | 1ne 92 | 1ni 93 | 1no 94 | 1nu 95 | 1ny 96 | 1n^^e4 97 | 1n^^f6 98 | 1pa 99 | 1pe 100 | 1pi 101 | 1po 102 | 1pu 103 | 1py 104 | 1p^^e4 105 | 1p^^f6 106 | 1ra 107 | 1re 108 | 1ri 109 | 1ro 110 | 1ru 111 | 1ry 112 | 1r^^e4 113 | 1r^^f6 114 | 1sa 115 | 1se 116 | 1si 117 | 1so 118 | 1su 119 | 1sy 120 | 1s^^e4 121 | 1s^^f6 122 | 1ta 123 | 1te 124 | 1ti 125 | 1to 126 | 1tu 127 | 1ty 128 | 1t^^e4 129 | 1t^^f6 130 | 1va 131 | 1ve 132 | 1vi 133 | 1vo 134 | 1vu 135 | 1vy 136 | 1v^^e4 137 | 1v^^f6 138 | % ------- Some common words borrowed from other languages ------- 139 | % ------- This part could be updated from time to time ------- 140 | % 141 | 1st2r % -stressi, -strategia etc. 142 | % 143 | % ------ Some special cases occuring with compound words only ---- 144 | % ------ There still remains well known problem as 'kaivos\-aukko' etc. 145 | %a1y (common in borrowed words) 146 | ^^e42y 147 | y1a2 148 | y1o2 149 | o1y 150 | ^^f62y 151 | u1y2 152 | y1u2 153 | ^^f63a2 154 | ^^f63o2 155 | ^^e43a2 156 | ^^e43o2 157 | ^^e41u2 158 | ^^f61u2 159 | a1^^e4 % (a1^^e42 ei mahdollinen!) 160 | a1^^f6 161 | o1^^e4 162 | o1^^f6 163 | u1^^e42 164 | u1^^f62 165 | ^^e42^^e4 166 | ^^f62^^f6 167 | ^^e42^^f6 168 | ^^f62^^e4 169 | % lyhyt/pitka -vokaalipareja, tavallisesti sanarajalla 170 | aa1i2 % maa-ikkuna 171 | aa1e2 172 | aa1o2 % maa-ottelu 173 | aa1u2 % uraa-uurtava 174 | ee1a2 % tee-astia 175 | ee1i2 % tee-istutus 176 | ee1u2 % varietee-uusinta 177 | ee1y2 178 | ii1a2 179 | ii1e2 180 | ii1o2 181 | uu1a2 182 | uu1e2 % puu-esine 183 | uu1o2 % puu-osa 184 | uu1i2 % puu-istutus 185 | e1aa 186 | i1aa 187 | o1aa 188 | u1aa 189 | u1ee 190 | a1uu % kala-uuni 191 | i1uu % ravi-uutiset 192 | e1uu % virhe-uutinen 193 | o1uu % radio-uutiset 194 | ^^e4^^e41i 195 | ^^e4^^e41e 196 | ^^e4^^e43y 197 | i1^^e4^^e4 198 | e1^^e4^^e4 199 | y1^^e4^^e4 200 | i1^^f6^^f6 % yhti-^^f6^^f6n etc. 201 | %i1eu % keski-eurooppalainen 202 | % vokaalikolmikkoja etc. yhdyssanojen rajoissa 203 | % -------- vowel triples by Fred Karlsson 204 | a1ei 205 | a1oi 206 | e1ai 207 | % e1oi % ambiguous for ex. video-ilme (8.3.89) 208 | i1au 209 | % u1oi % ambiguous (8.3.89) 210 | y1ei 211 | ai1a 212 | ai1e 213 | ai1o 214 | ai1u 215 | au1a 216 | au1e 217 | eu1a 218 | ie1a 219 | ie1o 220 | %ie1u % ambiguous 221 | ie1y 222 | io1a2 223 | io1e2 224 | iu1a 225 | iu1e 226 | iu1o 227 | oi1a 228 | oi1e 229 | oi1o 230 | oi1u 231 | o1ui % veto-uistin, himo-uimari etc. 232 | ou1e 233 | ou1o 234 | ue1a 235 | ui1e 236 | uo1a 237 | % uo1i % ambiguous 238 | uo1u 239 | % ---------------- End of vowel triples -------------------- 240 | e1^^f62 241 | ^^f61e2 242 | .^^e42 % don't hyphenate ^^e4-lyllinen etc. 243 | % 244 | % The following patterns contain no general scientific rule. They 245 | % are selected more or less intuitively to solve problems 246 | % with common and frequently appearing compound words. 247 | % However, every pattern resolves more than only one 248 | % hyphenation problem. 249 | % 250 | u2s % estaa virheita yhdyssanojen yhteydessa 251 | yli1o2p % yli-opisto etc. 252 | ali1a2v % ali-avaruus etc. 253 | 1sp2li % kuutio-splini etc. 254 | alous1 255 | keus1 % oikeus-oppinut etc. 256 | rtaus1 257 | 2s1ohje % -sohjelma etc. 258 | 2s1a2sia % nais-asianainen etc. 259 | 1a2sian % neuvottelu-asian etc. 260 | 1a2siat % koti-asian (ei kotia-sian) 261 | 1a2sioi % talous-asioita etc. 262 | r2as l2as % tikku-rasia etc 263 | 2s1o2pisk % xs-opiskelija etc 264 | 2n1o2pet 265 | 2s1a2loi 266 | 2n1o2pist % kansan-opisto etc. 267 | 2s1o2pist 268 | 2s1o2sa % xxs-osakas etc. 269 | 2n1o2sa % asian-osainen etc. 270 | alkei2s1 271 | perus1 272 | 2s1i2dea. 2s1i2dean 273 | 2s1e2sity % xs-esitys etc 274 | 2n1e2dus % kansan-edustaja etc. 275 | 2s1ajatu % -ajatus etc. 276 | 2s1ase 277 | 2s1apu 278 | 2s1y2rit % yhteis-yritys etc. 279 | .ydi2n1 280 | .suu2r1a2 % suur-ajot etc. 281 | 2s1y2hti 282 | 2n1otto 2n1oton 283 | 2n1anto 2n1anno 284 | 2n1a2jan 2n1aika 285 | 2n1o2mai 286 | 2n1y2lit 287 | 2s1a2len 288 | 2n1a2len 289 | 1a2siaka2s1 290 | ulo2s1 % ulos-ajo 291 | 2n1a2jo % kiven-ajo 292 | 2s1a2jo 293 | % 294 | % *** The following rules may be used on user's responsibility *** 295 | % *** for example, may be needed with narrow columns *** 296 | % >>>>>>>>>>> a1e a1o e1o o1a u1a <<<<<<<<<<< 297 | % 298 | % ----- Some districting rules by Professor Fred Karlsson's ideas ------ 299 | % 300 | b2l 1b2lo bib3li 301 | b2r 1b2ri 1b2ro 1b2ru 302 | d2r 1d2ra 303 | f2l 1f2la 304 | f2r 1f2ra 1f2re 305 | g2l 1g2lo 306 | g2r 1g2ra 307 | k2l 308 | 1k2ra 1k2re 1k2ri 309 | 1k2v 1k2va 310 | p2l 311 | p2r 1p2ro 312 | c2l 313 | q2v 1q2vi 314 | sc2h ts2h 315 | ch2r 316 | } 317 | \endgroup 318 | \endinput 319 | -------------------------------------------------------------------------------- /priv/hyphenation/original_tex/nohyphbx.tex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richcarl/erlguten/debd6120907e5e18b4bca5fbd5bdd9cb653f282d/priv/hyphenation/original_tex/nohyphbx.tex -------------------------------------------------------------------------------- /priv/hyphenation/tb87nemeth.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richcarl/erlguten/debd6120907e5e18b4bca5fbd5bdd9cb653f282d/priv/hyphenation/tb87nemeth.pdf -------------------------------------------------------------------------------- /priv/src/.gitignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | *.erl 3 | *.pfb 4 | -------------------------------------------------------------------------------- /priv/src/README.txt: -------------------------------------------------------------------------------- 1 | Font description module .erl files and Makefile will be generated here 2 | -------------------------------------------------------------------------------- /src/eg.hrl: -------------------------------------------------------------------------------- 1 | %%========================================================================== 2 | %% Copyright (C) 2003 Joe Armstrong 3 | %% 4 | %% Permission is hereby granted, free of charge, to any person obtaining a 5 | %% copy of this software and associated documentation files (the 6 | %% "Software"), to deal in the Software without restriction, including 7 | %% without limitation the rights to use, copy, modify, merge, publish, 8 | %% distribute, sublicense, and/or sell copies of the Software, and to permit 9 | %% persons to whom the Software is furnished to do so, subject to the 10 | %% following conditions: 11 | %% 12 | %% The above copyright notice and this permission notice shall be included 13 | %% in all copies or substantial portions of the Software. 14 | %% 15 | %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | %% OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | %% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 18 | %% NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 19 | %% DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 | %% OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 21 | %% USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | %%========================================================================== 23 | 24 | -record(env, {pdf, % PDF process 25 | currentBox, % name of currentBox 26 | template, % current template (Mopdule Name) 27 | page, % current page 28 | dict, % dictionary with keys{free,Page,Box} 29 | % {initialsed, Page, Template} 30 | tagMap % current TagMap (function of the box) 31 | }). 32 | 33 | -record(afm2, {baseFont, % FontName 34 | firstChar, % first char in widths table 35 | lastChar, % last char in Widths table 36 | widths, % the widths table (0=undefined) 37 | kernPairs, % [{{C1,C2},K}] 38 | ascender, % num 39 | capHeight, % num 40 | descender, % num 41 | flags, % num 42 | fontBBox, % {int,int,int,int} 43 | italicAngle, % num 44 | stemV, % num 45 | xHeight}). % num 46 | 47 | -record(info, {creator,creationDate, producer, author, title, subject, 48 | keywords}). 49 | 50 | -record(pdfContext, { 51 | info, 52 | fonts, 53 | font_handler, 54 | images=[], 55 | currentpage, 56 | pages=[], 57 | scripts=[], 58 | mediabox, 59 | procset={undefined,undefined} %% { imageb,imagec } 60 | }). 61 | 62 | 63 | -record(image,{alias,width,height}). 64 | 65 | -record(box,{x=10, % X coordinate of top left hand corner of box 66 | y=800, % Y coordinate of top left hand corner of box 67 | width=210, % Width in points (72 point=1 inch) 68 | leading=12, % leading in points 69 | lines=1, % # lines in box 70 | next=none, % next= none | {DeltaPage, Tag} 71 | free=1, % first free line in the box 72 | grid=false, % plot a grid 73 | bg=default % background color in box= default | {R,G,B} 74 | }). 75 | 76 | -record(tagMap, {name=default, % Tag name 77 | font="Times-Roman", % Font name 78 | color=default, % default | {R,G,B} 79 | size=11, % font size 80 | voff=0, % vertical offset 81 | break=true % can do line breaks in blanks 82 | }). 83 | 84 | 85 | -record(face, {font, pointSize, vOffset, color, breakable}). 86 | 87 | -------------------------------------------------------------------------------- /src/eg_embed.erl: -------------------------------------------------------------------------------- 1 | %%========================================================================== 2 | %% Copyright (C) 2003 Joe Armstrong 3 | %% 4 | %% Permission is hereby granted, free of charge, to any person obtaining a 5 | %% copy of this software and associated documentation files (the 6 | %% "Software"), to deal in the Software without restriction, including 7 | %% without limitation the rights to use, copy, modify, merge, publish, 8 | %% distribute, sublicense, and/or sell copies of the Software, and to permit 9 | %% persons to whom the Software is furnished to do so, subject to the 10 | %% following conditions: 11 | %% 12 | %% The above copyright notice and this permission notice shall be included 13 | %% in all copies or substantial portions of the Software. 14 | %% 15 | %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | %% OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | %% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 18 | %% NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 19 | %% DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 | %% OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 21 | %% USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | %% 23 | %% Author: Joe Armstrong 24 | %% Purpose: Font embedding code 25 | %%========================================================================== 26 | 27 | -module(eg_embed). 28 | 29 | -export([embed/1, 30 | parse_pfb/1 31 | ]). 32 | 33 | 34 | %% The format of a PBF is specified in 5040.Download_Fonts.pdf 35 | %% It is a sequence of chunks 36 | %% <<128, Type, N1, N2, N3, N4 >> <> 37 | %% <<128, 3>> 38 | 39 | %% Where 40 | %% Type = 1 -> ascii 41 | %% Type = 2 -> binary 42 | %% Len = N1 + N2*256 + N3*256*256 + N4*256*256*256, 43 | 44 | % test() -> 45 | % embed("brush.pfb"). 46 | 47 | embed(F) -> 48 | P = parse_pfb(F), 49 | O = lists:map(fun({_,_,B}) -> B end, P), 50 | file:write_file(F ++ ".synth", O). 51 | 52 | %% Parse_pfb -> [{Type,Len,Bin}] 53 | %% The lengths are the required lengths in the 54 | %% object descriptor ... 55 | 56 | parse_pfb(F) -> 57 | {ok, Bin} = file:read_file(F), 58 | _L = parse(Bin). 59 | 60 | parse(<<128,3>>) -> 61 | []; 62 | parse(B) -> 63 | {B1,B2} = split_binary(B, 6), 64 | [128,Type,N1,N2,N3,N4] = binary_to_list(B1), 65 | Len = N1 + N2*256 + N3*256*256 + N4*256*256*256, 66 | %% io:format("Chunk: ~p length=~p~n",[Type, Len]), 67 | case Len of 68 | 0 -> []; 69 | _ -> 70 | {B3,B4} = split_binary(B2, Len), 71 | [{Type,Len,B3}|parse(B4)] 72 | end. 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /src/eg_font_server.erl: -------------------------------------------------------------------------------- 1 | %%========================================================================== 2 | %% Copyright (C) 2003 Joe Armstrong 3 | %% 4 | %% Permission is hereby granted, free of charge, to any person obtaining a 5 | %% copy of this software and associated documentation files (the 6 | %% "Software"), to deal in the Software without restriction, including 7 | %% without limitation the rights to use, copy, modify, merge, publish, 8 | %% distribute, sublicense, and/or sell copies of the Software, and to permit 9 | %% persons to whom the Software is furnished to do so, subject to the 10 | %% following conditions: 11 | %% 12 | %% The above copyright notice and this permission notice shall be included 13 | %% in all copies or substantial portions of the Software. 14 | %% 15 | %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | %% OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | %% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 18 | %% NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 19 | %% DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 | %% OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 21 | %% USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | %% 23 | %% Authors: Joe Armstrong 24 | %%========================================================================== 25 | 26 | -module(eg_font_server). 27 | 28 | -include("eg.hrl"). 29 | 30 | 31 | -export([start/0, stop/0, char_width/2, data/1, info/1, kern/2]). 32 | 33 | %% ============================================================================ 34 | 35 | start() -> 36 | case lists:member(fonts, ets:all()) of 37 | true -> 38 | true; 39 | false -> 40 | fonts = ets:new(fonts, [named_table,set,public]), 41 | true 42 | end. 43 | 44 | stop() -> 45 | ets:delete(fonts). 46 | 47 | %% available_fonts() -> 48 | %% eg_afm:available_fonts(). 49 | 50 | %% ensure_loaded(Font, Index) -> 51 | %% %% io:format("Ensure_loaded font number=~p = ~s~n", [Index, Font]), 52 | %% case ets:lookup(fonts, {info, Index}) of 53 | %% [_] -> 54 | %% true; 55 | %% [] -> 56 | %% case eg_afm:read_font_info(Font) of 57 | %% {ok, {afm_qdh1,Font,Widths,Kern,All}} -> 58 | %% ets:insert(fonts, {{info, Index}, Font}), 59 | %% ets:insert(fonts, {{allData, Font}, All}), 60 | %% lists:foreach(fun({Char,W}) -> 61 | %% ets:insert(fonts, 62 | %% {{width,Index,Char}, W}) 63 | %% end, Widths), 64 | %% lists:foreach(fun({KP,W}) -> 65 | %% ets:insert(fonts, 66 | %% {{kern,Index,KP}, W}) 67 | %% end, Kern), 68 | %% true; 69 | %% {error, Why} -> 70 | %% exit({cannot_load_font, Why}) 71 | %% end 72 | %% end. 73 | 74 | info(Index) -> 75 | case ets:lookup(fonts, {info, Index}) of 76 | [{_,I}] -> 77 | I; 78 | [] -> 79 | exit({font_server_info,Index}) 80 | end. 81 | 82 | data(Fontname) -> 83 | case ets:lookup(fonts, {allData, Fontname}) of 84 | [{_,I}] -> 85 | {ok, I}; 86 | [] -> 87 | error 88 | end. 89 | 90 | char_width(N, Char) -> 91 | case ets:lookup(fonts, {width,N,Char}) of 92 | [{_,W}] -> 93 | W; 94 | [] -> 95 | io:format("Cannot figure out width of:~p ~p~n",[N, Char]), 96 | io:format("Possible \n in code etc~n"), 97 | 1000 98 | end. 99 | 100 | kern(N, KP) -> 101 | case ets:lookup(fonts, {kern,N,KP}) of 102 | [{_,W}] -> 103 | W; 104 | [] -> 105 | 0 106 | end. 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /src/eg_hyphenate.erl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richcarl/erlguten/debd6120907e5e18b4bca5fbd5bdd9cb653f282d/src/eg_hyphenate.erl -------------------------------------------------------------------------------- /src/eg_lib.erl: -------------------------------------------------------------------------------- 1 | %%========================================================================== 2 | %% Permission is hereby granted, free of charge, to any person obtaining a 3 | %% copy of this software and associated documentation files (the 4 | %% "Software"), to deal in the Software without restriction, including 5 | %% without limitation the rights to use, copy, modify, merge, publish, 6 | %% distribute, sublicense, and/or sell copies of the Software, and to permit 7 | %% persons to whom the Software is furnished to do so, subject to the 8 | %% following conditions: 9 | %% 10 | %% The above copyright notice and this permission notice shall be included 11 | %% in all copies or substantial portions of the Software. 12 | %% 13 | %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 | %% OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 15 | %% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 16 | %% NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | %% DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | %% OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 19 | %% USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | %%========================================================================== 21 | 22 | %% shared internal functions 23 | 24 | -module(eg_lib). 25 | 26 | -export([find_files/3, find_files/5, 27 | priv_dir/0, priv_src_dir/0]). 28 | 29 | -include_lib("kernel/include/file.hrl"). 30 | 31 | 32 | %% Examples: 33 | %% find_files(".", "*.erl", false) 34 | %% finds all files in the current directory. 35 | %% Recursive scan of sub-directories is also allowed. 36 | %% 37 | %% find_files(Dir, RegExp, Recursive, Fun/2, Acc0) 38 | %% applies Fun(File, Acc) -> Acc. to each file 39 | 40 | find_files(Dir, Re, Flag) -> 41 | Re1 = sh_to_awk(Re), 42 | lists:reverse(find_files(Dir, Re1, Flag, 43 | fun(File, Acc) ->[File|Acc] end, [])). 44 | 45 | find_files(Dir, Reg, Recursive, Fun, Acc) -> 46 | case file:list_dir(Dir) of 47 | {ok, Files} -> 48 | %% sort files of directory to make result stable; otherwise it 49 | %% depends on the file system and can vary between machines 50 | find_files(lists:sort(Files), Dir, Reg, Recursive, Fun, Acc); 51 | {error, _} -> Acc 52 | end. 53 | 54 | find_files([File|T], Dir, Reg, Recursive, Fun, Acc0) -> 55 | FullName = Dir ++ [$/|File], 56 | case file_type(FullName) of 57 | regular -> 58 | case re:run(FullName, Reg) of 59 | {match, _} -> 60 | Acc = Fun(FullName, Acc0), 61 | find_files(T, Dir, Reg, Recursive, Fun, Acc); 62 | nomatch -> 63 | find_files(T, Dir, Reg, Recursive, Fun, Acc0) 64 | end; 65 | directory -> 66 | case Recursive of 67 | true -> 68 | Acc1 = find_files(FullName, Reg, Recursive, Fun, Acc0), 69 | find_files(T, Dir, Reg, Recursive, Fun, Acc1); 70 | false -> 71 | find_files(T, Dir, Reg, Recursive, Fun, Acc0) 72 | end; 73 | error -> 74 | find_files(T, Dir, Reg, Recursive, Fun, Acc0) 75 | end; 76 | find_files([], _, _, _, _, A) -> 77 | A. 78 | 79 | file_type(File) -> 80 | case file:read_file_info(File) of 81 | {ok, Facts} -> 82 | case Facts#file_info.type of 83 | regular -> regular; 84 | directory -> directory; 85 | _ -> error 86 | end; 87 | _ -> 88 | error 89 | end. 90 | 91 | priv_src_dir() -> 92 | filename:join(priv_dir(), "src"). 93 | 94 | priv_dir() -> 95 | case code:priv_dir(erlguten) of 96 | {error, _} -> 97 | filename:join(filename:dirname(code:which(?MODULE)), 98 | "../priv"); 99 | N -> 100 | N 101 | end. 102 | 103 | %% NOTE: This function is copied from the deprecated stdlib 'regexp' module 104 | %% sh_to_awk(ShellRegExp) 105 | %% Convert a sh style regexp into a full AWK one. The main difficulty is 106 | %% getting character sets right as the conventions are different. 107 | 108 | sh_to_awk(Sh) -> "^(" ++ sh_to_awk_1(Sh). %Fix the beginning 109 | 110 | sh_to_awk_1([$*|Sh]) -> %This matches any string 111 | ".*" ++ sh_to_awk_1(Sh); 112 | sh_to_awk_1([$?|Sh]) -> %This matches any character 113 | [$.|sh_to_awk_1(Sh)]; 114 | sh_to_awk_1([$[,$^,$]|Sh]) -> %This takes careful handling 115 | "\\^" ++ sh_to_awk_1(Sh); 116 | sh_to_awk_1("[^" ++ Sh) -> [$[|sh_to_awk_2(Sh, true)]; 117 | sh_to_awk_1("[!" ++ Sh) -> "[^" ++ sh_to_awk_2(Sh, false); 118 | sh_to_awk_1([$[|Sh]) -> [$[|sh_to_awk_2(Sh, false)]; 119 | sh_to_awk_1([C|Sh]) -> 120 | %% Unspecialise everything else which is not an escape character. 121 | case special_char(C) of 122 | true -> [$\\,C|sh_to_awk_1(Sh)]; 123 | false -> [C|sh_to_awk_1(Sh)] 124 | end; 125 | sh_to_awk_1([]) -> ")$". %Fix the end 126 | 127 | sh_to_awk_2([$]|Sh], UpArrow) -> [$]|sh_to_awk_3(Sh, UpArrow)]; 128 | sh_to_awk_2(Sh, UpArrow) -> sh_to_awk_3(Sh, UpArrow). 129 | 130 | sh_to_awk_3([$]|Sh], true) -> "^]" ++ sh_to_awk_1(Sh); 131 | sh_to_awk_3([$]|Sh], false) -> [$]|sh_to_awk_1(Sh)]; 132 | sh_to_awk_3([C|Sh], UpArrow) -> [C|sh_to_awk_3(Sh, UpArrow)]; 133 | sh_to_awk_3([], true) -> [$^|sh_to_awk_1([])]; 134 | sh_to_awk_3([], false) -> sh_to_awk_1([]). 135 | 136 | %% Test if a character is a special character. 137 | special_char($|) -> true; 138 | special_char($*) -> true; 139 | special_char($+) -> true; 140 | special_char($?) -> true; 141 | special_char($() -> true; 142 | special_char($)) -> true; 143 | special_char($\\) -> true; 144 | special_char($^) -> true; 145 | special_char($$) -> true; 146 | special_char($.) -> true; 147 | special_char($[) -> true; 148 | special_char($]) -> true; 149 | special_char($") -> true; 150 | special_char(_C) -> false. 151 | -------------------------------------------------------------------------------- /src/eg_mk_hyphen.erl: -------------------------------------------------------------------------------- 1 | %%========================================================================== 2 | %% Copyright (C) 2003 Joe Armstrong 3 | %% 4 | %% Permission is hereby granted, free of charge, to any person obtaining a 5 | %% copy of this software and associated documentation files (the 6 | %% "Software"), to deal in the Software without restriction, including 7 | %% without limitation the rights to use, copy, modify, merge, publish, 8 | %% distribute, sublicense, and/or sell copies of the Software, and to permit 9 | %% persons to whom the Software is furnished to do so, subject to the 10 | %% following conditions: 11 | %% 12 | %% The above copyright notice and this permission notice shall be included 13 | %% in all copies or substantial portions of the Software. 14 | %% 15 | %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | %% OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | %% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 18 | %% NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 19 | %% DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 | %% OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 21 | %% USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | %% 23 | %% Author: Joe Armstrong 24 | %%========================================================================== 25 | 26 | %% ============================================================================ 27 | %% load hyph_*.dic files and create matching eg_hyphen_rules_*.erl 28 | %% ============================================================================ 29 | 30 | -module(eg_mk_hyphen). 31 | 32 | -export([start/1, 33 | start/2 34 | ]). 35 | 36 | %% ============================================================================ 37 | 38 | %% takes a list of atoms as input, to be called as "erl -s eg_mk_hyphen CC" 39 | start([CC]) -> 40 | start(lists:concat(["hyph_", CC, ".dic"]), CC). 41 | 42 | %% Pass .dic file name and lc_CC (language code and country code) to use 43 | %% in generated eg_hyphen_rules_lc_CC.erl files. 44 | %% 45 | %% * File is expected to start with a line "charset ISO8859-1 ..." or 46 | %% "ISO8859-1 ...", ensuring that the file contains Latin-1 encoded text. 47 | %% * Each NL or CR separated line may contain one hyphen rule. 48 | %% * Trailing and intial spaces are discarded before the rule is processed. 49 | %% * Anything after and including a "%" is treated as a comment an therfore 50 | %% discarded. Lines that are or become blank this way are also discarded. 51 | %% * "\hyphenation" is treated as a special rule that tells us that all rules 52 | %% following it are exceptions to the normal hyphen rules - each such rules 53 | %% contains one hyphenated word e.g. "uni-ver-sity" 54 | %% 55 | %% FileName = string() 56 | %% LangCodeStr = string() 57 | start(FileName, LangCode) -> 58 | {ok, Bin} = file:read_file(filename:join([eg_lib:priv_dir(), 59 | "hyphenation", FileName])), 60 | L = binary_to_list(Bin), 61 | Toks = string:tokens(L, "\n\r"), 62 | %% get lines after initial header 63 | L1 = lines_after_header(Toks), 64 | 65 | %% Find hyphen rules and exceptions 66 | {L2, L3} = split(clear_comments_and_sp(L1)), 67 | 68 | ModuleName = lists:concat(["eg_hyphen_rules_", LangCode]), 69 | {ok, O} = file:open(filename:join([eg_lib:priv_src_dir(), 70 | ModuleName ++ ".erl"]), 71 | [write]), 72 | io:format(O, "%% -*- coding: latin-1 -*-~n", []), 73 | io:format(O, "%% autogenerated do not edit~n",[]), 74 | io:format(O, "%%[{Char,Val}] = after char Char Val is inserted~n",[]), 75 | io:format(O, "-module(" ++ ModuleName ++ ").~n", []), 76 | io:format(O, "-export([hyphens/1, exception/1]).~n~n",[]), 77 | 78 | %% * sort to ensure that alphabetical order 79 | %% * reverse so that exception(...) clauses are generated with the largest 80 | %% match first 81 | Except = lists:reverse(lists:sort(L3)), 82 | lists:foreach(fun(I) -> mk_exception(O, I) end, Except), 83 | io:format(O, "exception(_) -> no.~n~n", []), 84 | 85 | %% * sort to ensure that alphabetical order 86 | %% * reverse so that hyphens(...) clauses are generated with the largest 87 | %% match first 88 | HyphenClauses1 = [jiggle(I) || I <- L2], 89 | HyphenClauses2 = lists:reverse(lists:sort(HyphenClauses1)), 90 | lists:foreach( 91 | fun({S1,S2}) -> io:format(O, "hyphens(\"~s\" ++ _)->~p;~n",[S1,S2]) end, 92 | HyphenClauses2), 93 | io:format(O, "hyphens(_) -> [].~n",[]), 94 | 95 | file:close(O). 96 | 97 | 98 | %% keep only non-empty non-comment content lines 99 | clear_comments_and_sp(Lines) -> 100 | ClearedLines = [string:strip(clear_comments(Line)) || Line <- Lines], 101 | %% discard empty lines 102 | [Line || Line <- ClearedLines, Line /= ""]. 103 | 104 | 105 | %% "% ...." -> "" 106 | %% " foo bar % foo bar" -> " foo bar " 107 | clear_comments([]) -> []; 108 | clear_comments([$% | _]) -> []; 109 | clear_comments([C | R]) -> [C | clear_comments(R)]. 110 | 111 | %% @split(RuleLines::[string()]) -> {HyhpenRules::[string()], 112 | %% Exceptions::[string()]} 113 | %% extract the hyphen rules and rule exception lines 114 | split(RuleLines) -> 115 | split(RuleLines, []). 116 | 117 | %% only found hyphen rules 118 | split([], HyphenRules) -> {lists:reverse(HyphenRules), []}; 119 | %% hyphen rules and rule exceptions found 120 | split(["\\hyphenation" | ExcpL], HyphenRules) -> {lists:reverse(HyphenRules), 121 | ExcpL}; 122 | split(["\\" ++ _ | _ExcpL], _) -> throw(unkown_dic_syntax); 123 | split([HyphenRule | R], HyphenRules) -> split(R, [HyphenRule | HyphenRules]). 124 | 125 | 126 | mk_exception(Stream, H) -> 127 | X = remove_hyphens(H), 128 | io:format(Stream, "exception(\"~s\") -> \"~s\";~n",[X, H]). 129 | 130 | remove_hyphens([$-|T]) -> remove_hyphens(T); 131 | remove_hyphens([H|T]) -> [H|remove_hyphens(T)]; 132 | remove_hyphens([]) -> []. 133 | 134 | 135 | jiggle(X) -> 136 | jiggle(X, 0, [], []). 137 | 138 | jiggle([], _, L1, L2) -> 139 | {lists:reverse(L1), L2}; 140 | jiggle([H|T], N, L1, L2) when H >= $1, H =< $9 -> 141 | jiggle(T, N, L1, [{N,H-$0}|L2]); 142 | jiggle([$0|T], N, L1, L2) -> 143 | io:format("****?~n"), 144 | jiggle(T, N, L1, L2); 145 | jiggle([H|T], N, L1, L2) -> 146 | jiggle(T, N+1, [H|L1], L2). 147 | 148 | 149 | %% we allow any header that ends with a "charset ISO8859-1 ...." or 150 | %% "ISO8859-1 ...." line 151 | lines_after_header(["charset ISO8859-1" ++ _|T]) -> T; 152 | lines_after_header(["ISO8859-1" ++ _|T]) -> T; 153 | lines_after_header([_|T]) -> lines_after_header(T). 154 | 155 | -------------------------------------------------------------------------------- /src/eg_pdf_annot.erl: -------------------------------------------------------------------------------- 1 | %%========================================================================== 2 | %% Copyright (C) 2003 Mikael Karlsson 3 | %% 4 | %% Permission is hereby granted, free of charge, to any person obtaining a 5 | %% copy of this software and associated documentation files (the 6 | %% "Software"), to deal in the Software without restriction, including 7 | %% without limitation the rights to use, copy, modify, merge, publish, 8 | %% distribute, sublicense, and/or sell copies of the Software, and to permit 9 | %% persons to whom the Software is furnished to do so, subject to the 10 | %% following conditions: 11 | %% 12 | %% The above copyright notice and this permission notice shall be included 13 | %% in all copies or substantial portions of the Software. 14 | %% 15 | %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | %% OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | %% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 18 | %% NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 19 | %% DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 | %% OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 21 | %% USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | %% 23 | %% Author: Mikael Karlsson 24 | %% Purpose: PDF Annotations 25 | %%========================================================================== 26 | 27 | %% @doc Annotations. 28 | %% 29 | %%

Purpose: Create and serialise PDF Annotations

30 | %%

Ref. Chapter 8.4 Annotations in PDF reference v1.4

31 | %% 32 | %%

33 | %%

34 | %%
/Type
/Annot
35 | %%
/Subtype
name, Required
36 | %%
/Contents
37 | %%
text string. Required or optional depending on subtype
38 | %%
/P
39 | %%
dictionary. Optional indirect ref. to the page object
40 | %%
/Rect
rectangle. Required
41 | %%
/NM
text string. Optional, annotation name
42 | %%
/M
43 | %%
Date or string. Optional, last modified date and time
44 | %%
/F
45 | %%
Integer. optional, Flags specifying various characteristics
46 | %%
/BS
Dictionary. Optional, Border style
47 | %%
/Border
48 | %%
Array. Optional, annotation border characteristics. 49 | %% Use bs inst.
50 | %%
/AP
Dictionary. Appearance
51 | %%
/AS
52 | %%
Name. Required if ap contains one or more subdirectories. 53 | %% Appearance state, selects applicable appearance stream.
54 | %%
/C
55 | %%
Array of 3 numbers range 0-1. Optional. RGB color, for icon etc.
56 | %%
/CA
57 | %%
Number 0-1, Optional. Constant opacity value 0=no contr. 1=full
58 | %%
/T
Text string. Optional. Text label in title bar.
59 | %%
/Popup
60 | %%
Dictionary. Optional. Indirect reference to popup annot for edit.
61 | %%
/A
62 | %%
Dictionary. Optional. Action to be performed when activated.
63 | %%
/AA
Dictionary. Optional. Additional actions
64 | %%
/StructParent
65 | %%
Integer. Required if annotation is a structural content item.
66 | %%
67 | %%

68 | %% @end 69 | 70 | -module(eg_pdf_annot). 71 | 72 | -include("eg.hrl"). 73 | 74 | 75 | -export([new_text/2, new_text/3, new_open_text/2, new_open_text/3, 76 | new_link/2, new_link/3, new_action/2, new_line/3, set_flags/2, 77 | andflags/2, orflags/2, contents/1, flag/1, isflagset/2, rect/1, 78 | set_borderstyle/3, style/1, serialise/2]). 79 | 80 | %% ============================================================================ 81 | 82 | %% Subtypes 83 | subtype(text) -> {name, "Text"}; 84 | subtype(link) -> {name, "Link"}; 85 | subtype(freetext) -> {name, "FreeText"}; 86 | subtype(line) -> {name, "Line"}; 87 | subtype(square) -> {name, "Square"}; 88 | subtype(circle) -> {name, "Circle"}; 89 | subtype(highlight) -> {name, "Highlight"}; 90 | subtype(underline) -> {name, "Underline"}; 91 | subtype(squiggly) -> {name, "Squiggly"}; 92 | subtype(strikeout) -> {name, "StrikeOut"}; 93 | subtype(stamp) -> {name, "Stamp"}; 94 | subtype(ink) -> {name, "Ink"}; 95 | subtype(popup) -> {name, "Popup"}; 96 | subtype(fileattachement) -> {name, "FileAttacheMent"}; 97 | subtype(sound) -> {name, "Sound"}; 98 | subtype(movie) -> {name, "Movie"}; 99 | subtype(widget) -> {name, "Widget"}; 100 | subtype(printermark) -> {name, "PrinterMark"}; 101 | subtype(trapnet) -> {name, "TrapNet"}. 102 | 103 | contents(Contents) -> {string, Contents}. 104 | 105 | %% @type rect() = {X, Y, Width, Height} 106 | rect({A,B,C,D}) -> {array, [A,B,C,D]}. 107 | 108 | %% Flags 109 | flag(invisible) -> 1; 110 | flag(hidden) -> 2; 111 | flag(print) -> 4; 112 | flag(nozoom) -> 8; 113 | flag(norotate) -> 16; 114 | flag(noview) -> 32; 115 | flag(readonly) -> 64. 116 | 117 | 118 | %% @spec new(Subtype, Rect) -> dict() 119 | %% Rect = {rect, {X,Y, Width, Height}} 120 | new(SubType, {rect,{_X,_Y,_W,_H}} = Rect)-> 121 | {dict,[{"Type",{name,"Annot"}}, 122 | {"Subtype", subtype(SubType)}, 123 | {"Rect", Rect }]}. 124 | 125 | new(SubType, Rect, Contents)-> 126 | A = new(SubType, Rect), 127 | set_contents(Contents,A). 128 | 129 | %% @spec new_text(Rect::rect(), Contents::string()) -> 130 | %% text_annotation() 131 | %% @doc Text annotations represents a sticky note 132 | 133 | new_text(Rect, Contents)-> 134 | new(text,Rect,Contents). 135 | 136 | %% Default when Name is not given is Note 137 | %% @spec new_text(Rect::rect(), Contents::string(), name()) -> 138 | %% text_annotation() 139 | new_text(Rect, Contents, Name)-> 140 | A = new_text(Rect, Contents), 141 | store("Name", name(Name), A). 142 | 143 | %% @spec new_open_text(Rect::rect(), Contents::string()) -> 144 | %% text_annotation() 145 | %% @doc Text annotation that is open 146 | 147 | new_open_text(Rect, Contents)-> 148 | A = new_text(Rect, Contents), 149 | store( "Open", true, A ). 150 | 151 | %% @spec new_open_text(Rect::rect(), Contents::string(), name()) -> 152 | %% text_annotation() 153 | new_open_text(Rect, Contents, Name)-> 154 | A = new_text(Rect, Contents), 155 | B = store("Name", name(Name), A), 156 | store( "Open", true, B ). 157 | 158 | %% @type name() = comment|help|insert|key|note|paragraph 159 | 160 | name(comment) -> {name, "Comment"}; 161 | name(help) -> {name, "Help"}; 162 | name(insert) -> {name, "Insert"}; 163 | name(key) -> {name, "Key"}; 164 | name(newparagraph)-> {name, "NewParagraph"}; 165 | name(note) -> {name, "Note"}; 166 | name(paragraph) -> {name, "Paragraph"}. 167 | 168 | 169 | %% ------ Link annotations are either hyper text links 170 | %% or actions to performed 171 | %% @spec new_link(Rect::rect(), Dest::destination()) -> dict() 172 | new_link(Rect, Dest)-> 173 | A = new(link,Rect), 174 | %% [ PageNo | T ] = Dest, 175 | store("Border",{array,[16,16,1]}, store("Dest", Dest, A )). 176 | 177 | 178 | 179 | %% @spec new_link(Rect::rect(), Dest::destination(), Highlight) -> dict() 180 | %% Highlight = none | invert | outline | push 181 | 182 | new_link(Rect, Dest, Highlight)-> 183 | A = new_link(Rect, Dest), 184 | store( "H", highlight(Highlight), A ). 185 | 186 | 187 | 188 | highlight(none) -> {name, "N"}; 189 | highlight(invert) -> {name, "I"}; %% Default 190 | highlight(outline) -> {name, "O"}; 191 | highlight(push) -> {name, "P"}. 192 | 193 | 194 | %% Action is a predefined action dictionary 195 | new_action(Rect, {action, Action})-> 196 | A = new(link, Rect), 197 | store("A", {action, Action}, A). 198 | 199 | %% ------ Line annotations 200 | new_line(Rect, Contents, {Point1, Point2})-> 201 | A = new(line,Rect, Contents), 202 | {X1, Y1} = Point1, {X2, Y2} = Point2, 203 | store("L", {array,[X1, Y1, X2, Y2]}, A ). 204 | 205 | 206 | set_contents(Contents, Annot)-> 207 | store("Contents", {string,Contents}, Annot). 208 | 209 | store(Key, Value, Dict)-> 210 | eg_pdf_lib:store_in_dict({Key, Value}, Dict). 211 | 212 | 213 | set_borderstyle(Width, Style, Annot)-> 214 | S = style(Style), 215 | A = {dict,[{"Type",{name,"Border"}}, 216 | {"W", Width}, 217 | {"S", S}]}, 218 | store("BS", A, Annot). 219 | 220 | style(solid) -> {name, "S"}; 221 | style(dashed) -> {name, "D"}; 222 | style(beveled) -> {name, "B"}; 223 | style(inset) -> {name, "I"}; 224 | style(underline) -> {name, "U"}. 225 | 226 | %% Annotation flags 227 | 228 | orflags(Flag, Flags)-> 229 | flag(Flag) bor Flags. 230 | 231 | andflags(Flag, Flags)-> 232 | flag(Flag) band Flags. 233 | 234 | isflagset(Flag, Flags)-> 235 | case andflags(Flag, Flags) of 236 | 0 -> 237 | false; 238 | _ -> 239 | true 240 | end. 241 | 242 | set_flags(Flags, Annot)-> 243 | store("F", Flags, Annot). 244 | 245 | 246 | 247 | serialise(Annots, I)-> 248 | serialise(Annots, I, [], []). 249 | 250 | serialise([], I, Is, Os) -> 251 | A = {{obj,I,0},{array,lists:map(fun(J) -> 252 | {ptr,J,0} 253 | end, 254 | lists:reverse(Is))}}, 255 | {I+1, {ptr,I,0}, lists:reverse([A|Os])}; 256 | serialise([H|T], I, Fs, E) -> 257 | O = mk_annot(I, H), 258 | serialise(T, I+1, [I|Fs], [O|E]). 259 | 260 | mk_annot(I, {annot,A}) -> 261 | {{obj,I,0}, 262 | {dict,dict:to_list(A)} 263 | }. 264 | -------------------------------------------------------------------------------- /src/eg_pdf_image.erl: -------------------------------------------------------------------------------- 1 | %%========================================================================== 2 | %% Copyright (C) 2003 Mikael Karlsson 3 | %% 4 | %% Permission is hereby granted, free of charge, to any person obtaining a 5 | %% copy of this software and associated documentation files (the 6 | %% "Software"), to deal in the Software without restriction, including 7 | %% without limitation the rights to use, copy, modify, merge, publish, 8 | %% distribute, sublicense, and/or sell copies of the Software, and to permit 9 | %% persons to whom the Software is furnished to do so, subject to the 10 | %% following conditions: 11 | %% 12 | %% The above copyright notice and this permission notice shall be included 13 | %% in all copies or substantial portions of the Software. 14 | %% 15 | %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | %% OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | %% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 18 | %% NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 19 | %% DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 | %% OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 21 | %% USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | %% 23 | %% Author: Mikael Karlsson 24 | %% Purpose: Import images to PDF 25 | %%========================================================================== 26 | 27 | -module(eg_pdf_image). 28 | 29 | %% Purpose: Import images from file, currently jpg only 30 | %% Pack into XObjects 31 | %% 32 | 33 | -export([mk_images/4, get_head_info/1, process_header/1]). 34 | 35 | -include("eg.hrl"). 36 | 37 | %% ============================================================================ 38 | 39 | 40 | mk_images([], I, Is, Os) -> 41 | A = {{obj,I,0},{dict,lists:map(fun({Alias, ImageIndex}) -> 42 | {Alias, {ptr,ImageIndex,0}} 43 | end, 44 | lists:reverse(Is))}}, 45 | {I+1, {ptr,I,0}, lists:reverse([A|Os])}; 46 | mk_images([{ImageURI, #image{alias=Alias}=Im}|T], I, Fs, E) -> 47 | O = mk_image(I, ImageURI, Im), 48 | mk_images(T, I+1, [{Alias,I}|Fs], [O|E]). 49 | 50 | mk_image(I, File, #image{alias=_Alias, width=W, height=H}) -> 51 | Image = read_image(File), 52 | case process_header(Image) of 53 | {jpeg_head,{Width, Height, Ncomponents, Data_precision}} -> 54 | Extras = [{"Filter", {name,"DCTDecode"}}, 55 | {"ColorSpace",{name, colorspace(Ncomponents)}}, 56 | {"BitsPerComponent", Data_precision}]; 57 | _ -> 58 | {Width, Height} = {W,H}, 59 | Extras = [] 60 | end, 61 | {{obj,I,0}, 62 | {stream, 63 | {dict,[{"Type",{name,"XObject"}}, 64 | {"Subtype",{name,"Image"}}, 65 | {"Width",Width}, 66 | {"Height", Height}| Extras ]}, 67 | Image} 68 | }. 69 | 70 | 71 | colorspace(0)-> "DeviceGray"; 72 | colorspace(1)-> "DeviceGray"; 73 | colorspace(2)-> "DeviceGray"; 74 | colorspace(3)-> "DeviceRGB"; 75 | colorspace(4)-> "DeviceCMYK". 76 | 77 | read_image(File) -> 78 | {ok, Image} = file:read_file(File), 79 | Image. 80 | 81 | get_head_info(File) -> 82 | process_header(read_image(File)). 83 | 84 | %% JPEG support 85 | -define(SOF0, 16#C0). %% Start Of Frame 86 | -define(SOF1, 16#C1). %% Digit indicates compression type 87 | -define(SOF2, 16#C2). %% Only SOF0-SOF2 are now in common use 88 | -define(SOF3, 16#C3). 89 | -define(SOF5, 16#C5). 90 | -define(SOF6, 16#C6). 91 | -define(SOF7, 16#C7). 92 | -define(SOF9, 16#C9). 93 | -define(SOF10, 16#CA). 94 | -define(SOF11, 16#CB). 95 | -define(SOF13, 16#CD). 96 | -define(SOF14, 16#CE). 97 | -define(SOF15, 16#CF). 98 | -define(SOI, 16#D8). %% Start Of Image (beginning of file) 99 | -define(EOI, 16#D9). %% End Of Image 100 | -define(SOS, 16#DA). %% Start Of Scan (begin of compressed data) 101 | %% -define(APP0, 16#E0). %% Application-specific markers, don't bother 102 | %% -define(APP12, 16#EC). 103 | %% -define(COM, 16#FE). %% Comment 104 | 105 | 106 | %% JPEG file 107 | process_header( << 16#FF:8, ?SOI:8, Rest/binary >> )-> 108 | process_jpeg( Rest ); 109 | process_header(_Any) -> 110 | image_format_not_yet_implemented_or_unknown. 111 | 112 | 113 | %% JPEG file header processing 114 | 115 | %% Skip any leading 16#FF 116 | process_jpeg( << 16#FF:8,Rest/binary >> ) -> process_jpeg(Rest); 117 | 118 | %% Codes 0xC4, 0xC8, 0xCC shall not be treated as SOFn. 119 | process_jpeg( << ?SOF0:8,Rest/binary >> ) -> jpeg_sof( Rest); 120 | process_jpeg( << ?SOF1:8,Rest/binary >> ) -> jpeg_sof( Rest); 121 | process_jpeg( << ?SOF2:8,Rest/binary >> ) -> jpeg_sof( Rest); 122 | process_jpeg( << ?SOF3:8,Rest/binary >> ) -> jpeg_sof( Rest); 123 | process_jpeg( << ?SOF5:8,Rest/binary >> ) -> jpeg_sof( Rest); 124 | process_jpeg( << ?SOF6:8,Rest/binary >> ) -> jpeg_sof( Rest); 125 | process_jpeg( << ?SOF7:8,Rest/binary >> ) -> jpeg_sof( Rest); 126 | process_jpeg( << ?SOF9:8,Rest/binary >> ) -> jpeg_sof( Rest); 127 | process_jpeg( << ?SOF10:8,Rest/binary >> ) -> jpeg_sof(Rest); 128 | process_jpeg( << ?SOF11:8,Rest/binary >> ) -> jpeg_sof(Rest); 129 | process_jpeg( << ?SOF13:8,Rest/binary >> ) -> jpeg_sof(Rest); 130 | process_jpeg( << ?SOF14:8,Rest/binary >> ) -> jpeg_sof(Rest); 131 | process_jpeg( << ?SOF15:8,Rest/binary >> ) -> jpeg_sof(Rest); 132 | process_jpeg( << ?SOS:8,_Rest/binary >> ) ->[]; 133 | process_jpeg( << ?EOI:8,_Rest/binary >> ) -> jeoi; %% Tables only 134 | process_jpeg( << _Any:8, Rest/binary >> )->process_jpeg(skip_marker(Rest)); 135 | process_jpeg( << >> ) -> []. 136 | 137 | jpeg_sof ( Rest )-> 138 | << Length:16, Data_precision:8, Height:16, Width:16, 139 | Ncomponents:8, Rest2/binary >> = Rest, Complen = Ncomponents * 3, 140 | Length = Complen + 8, %% This is a guard that will cause an error exception 141 | << _SkipComponents:Complen/binary, _Rest3/binary >> = Rest2, 142 | {jpeg_head,{Width, Height, Ncomponents, Data_precision}}. 143 | %% process_jpeg(Rest3)]. 144 | 145 | skip_marker(Image)-> 146 | << Length:16, Rest/binary >> = Image, 147 | AdjLen = Length-2, 148 | << _Skip:AdjLen/binary, Rest2/binary >> = Rest, 149 | Rest2. 150 | -------------------------------------------------------------------------------- /src/eg_pdf_obj.erl: -------------------------------------------------------------------------------- 1 | %%========================================================================== 2 | %% Copyright (C) 2003 Mikael Karlsson 3 | %% 4 | %% Permission is hereby granted, free of charge, to any person obtaining a 5 | %% copy of this software and associated documentation files (the 6 | %% "Software"), to deal in the Software without restriction, including 7 | %% without limitation the rights to use, copy, modify, merge, publish, 8 | %% distribute, sublicense, and/or sell copies of the Software, and to permit 9 | %% persons to whom the Software is furnished to do so, subject to the 10 | %% following conditions: 11 | %% 12 | %% The above copyright notice and this permission notice shall be included 13 | %% in all copies or substantial portions of the Software. 14 | %% 15 | %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | %% OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | %% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 18 | %% NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 19 | %% DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 | %% OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 21 | %% USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | %% 23 | %% Authors: Mikael Karlsson 24 | %% Joe Armstrong 25 | %% Purpose: PDF objects api 26 | %%========================================================================== 27 | 28 | %% 29 | %% @doc PDF Objects. 30 | %% 31 | %%

Purpose: Create and manipulate PDF Objects

32 | %%

33 | %% 34 | %% @end 35 | 36 | -module(eg_pdf_obj). 37 | 38 | -export([catalogue/2, info/1, destination/2, fonts/2]). 39 | 40 | %% ============================================================================ 41 | 42 | %% @spec catalogue(PagesRef::integer(), Options) -> dict() 43 | %% Options = [Option] 44 | %% Option = {'"Version"', name()} | {'"PageLabels"', number_tree()} | 45 | %% {'"Names"', dict()} 46 | %% Ref = integer() 47 | %% @doc Creates a Catalogue dictionary. 48 | %% @see eg_pdf_lib 49 | catalogue(PagesRef, Options) -> 50 | {dict,[{"Type",{name,"Catalog"}}, 51 | {"Pages",{ptr, PagesRef,0}}| 52 | lists:map( fun catalogue_opt/1, Options)]}. 53 | catalogue_opt({"Version" = A, {name, _}} = B) -> 54 | {A, B}; 55 | catalogue_opt({"PageLabels" = A, {number_tree, _ } = B}) -> 56 | {A, B}; 57 | catalogue_opt({"Names" = A, {dict, _ } = B}) -> 58 | {A, B}. 59 | 60 | 61 | %% @spec info(Options) -> dict() 62 | %% Options = [Option] 63 | %% Option = {'"Creator"', string()} | {'"CreationDate"', Date} | 64 | %% {'"Producer"', string()} | {'"Author"', string()} | {'"Title"', string()} | 65 | %% {'"Subject"', string()} | {'"Keywords"', string()} 66 | %% @doc Creates a PDF Info dictionary. 67 | info(Options) -> 68 | {dict, lists:map( fun info_opt/1, Options)}. 69 | 70 | info_opt({"Creator" = A, B}) -> 71 | { A, {string, B}}; 72 | info_opt({"CreationDate" = A, B }) -> 73 | { A, {date, B}}; 74 | info_opt({"Producer" = A, B}) -> 75 | { A, {string, B}}; 76 | info_opt({"Author"= A, B}) -> 77 | { A, {string, B}}; 78 | info_opt({"Title"= A, B}) -> 79 | { A, {string, B}}; 80 | info_opt({"Subject" = A, B}) -> 81 | { A , {string, B}}; 82 | info_opt({"Keywords" = A, B}) -> 83 | { A, {string, B}}. 84 | 85 | 86 | 87 | %% @spec destination(PageRef::integer(), BoundBox) -> array() 88 | %% BoundBox = {'"XYZ"', Left, Top, Zoom } | '"Fit"' | 89 | %% {'"FitH"', Top} | {'"FitV"', Left} | 90 | %% {'"FitR"', Left, Bottom, Right, Top} | '"FitB"' | 91 | %% {'"FitBH"', Top} | {'"FitBV"', Left} 92 | %% Top = integer() 93 | %% Bottom = integer() 94 | %% Right = integer() 95 | %% Bottom = integer() 96 | %% Zoom = null | number() 97 | %% @doc Creates a destination array. 98 | %% PageRef is the indirect reference Ptr to the Page Object. 99 | %% see also: PDF reference chapter 8.2.1 Destinations 100 | destination(PageRef, {"XYZ", Left, Top, Zoom }) -> 101 | {array,[{ptr, PageRef, 0}, {name,"XYZ"}, Left, Top, Zoom ]}; 102 | destination(PageRef, "Fit") -> 103 | {array,[{ptr, PageRef, 0}, {name,"Fit"}]}; 104 | destination(PageRef, {"FitH", Top}) -> 105 | {array,[{ptr, PageRef, 0}, {name,"FitH"}, Top ]}; 106 | destination(PageRef, {"FitV", Left}) -> 107 | {array,[{ptr, PageRef, 0}, {name,"FitV"}, Left ]}; 108 | destination(PageRef, {"FitR", Left, Bottom, Right, Top}) -> 109 | {array,[{ptr, PageRef, 0}, {name,"FitR"}, Left, Bottom, Right, Top ]}; 110 | destination(PageRef, "FitB") -> 111 | {array,[{ptr, PageRef, 0}, {name,"FitB"} ]}; 112 | destination(PageRef, {"FitBH", Top}) -> 113 | {array,[{ptr, PageRef, 0}, {name,"FitBH"}, Top ]}; 114 | destination(PageRef, {"FitBV", Left}) -> 115 | {array,[{ptr, PageRef, 0}, {name,"FitBH"}, Left ]}. 116 | 117 | %% ========================================================== 118 | %% Fonts 119 | %% ========================================================== 120 | 121 | %% @spec fonts(Fonts, Objects::pdftype()) -> {FontsPtr, Objects} 122 | 123 | fonts(Fonts, Objects) -> 124 | Free0 = eg_pdf_lib:get_next_ref(Objects), 125 | Fonts1 = lists:map(fun(I) -> eg_font_map:handler(I) end, Fonts), 126 | {_Free,FontsPtr,O1s} = mk_fonts(Fonts1, Free0, [], []), 127 | {FontsPtr, eg_pdf_lib:store_object(O1s, Objects)}. 128 | 129 | mk_fonts([], I, Fs, Os) -> 130 | A = {{obj,I,0},{dict,lists:map(fun({Alias, FontObj}) -> 131 | {Alias, {ptr,FontObj,0}} 132 | end, lists:reverse(Fs))}}, 133 | {I+1, {ptr,I,0}, lists:reverse([A|Os])}; 134 | mk_fonts([Handler|T], I, Fs, E) -> 135 | %% io:format("I need the font:~p~n",[Handler]), 136 | Index = Handler:index(), 137 | Alias = "F" ++ eg_pdf_op:i2s(Index), 138 | case Handler:type() of 139 | internal -> 140 | O = {{obj,I,0},mkFont(Handler)}, 141 | mk_fonts(T, I+1, [{Alias,I}|Fs], [O|E]); 142 | {Index, pdf_builtin} -> 143 | O1 = {{obj,I,0}, mkFont1(Handler, I+1, Index)}, 144 | O2 = {{obj,I+1,0}, mkFontDescriptor(Handler, false, 0)}, 145 | mk_fonts(T, I+2, [{Alias,I}|Fs], [O2,O1|E]); 146 | external -> 147 | O1 = {{obj,I,0}, mkFont1(Handler, I+1, Index)}, 148 | O2 = {{obj,I+1,0}, mkFontDescriptor(Handler, true,I+2)}, 149 | O3 = {{obj,I+2,0}, mkFontFile(Handler)}, 150 | mk_fonts(T, I+3, [{Alias,I}|Fs], [O3,O2,O1|E]) 151 | end. 152 | 153 | %% mkFont is used for the 14 inbuilt fonts 154 | mkFont(FontHandler) -> 155 | Index = FontHandler:index(), 156 | Alias = "F" ++ eg_pdf_op:i2s(Index), 157 | %% io:format("mkFont Alias=~s FontHandler=~p~n",[Alias, FontHandler]), 158 | {dict,[{"Type",{name,"Font"}}, 159 | {"Subtype",{name,"Type1"}}, 160 | {"Name",{name,Alias}}, 161 | {"BaseFont",{name,FontHandler:fontName()}}, 162 | {"Encoding",{name,encoding(FontHandler)}}]}. 163 | 164 | encoding(M) -> 165 | %% Change the encoding to "MacRomanEncoding" except for 166 | %% "FontSpecific" encodings ... 167 | %% This seems to work for everything except those fonts 168 | %% which have a "FontSpecif" encoding. 169 | %% *usally the encoding in the AFM file is 170 | %% "AdobeStandardEncoding" - but this gives an error 171 | %% for fonts with encoding "AppleStandard". Setting 172 | %% *everything* to MacRomanEncoding seems to work for all cases 173 | %% except Zapfdingblats which is "FontSpecific" 174 | %% - this might not work with files produced on an apple ? 175 | %% - I have not yet tested this on an apple 176 | case M:encoding() of 177 | S = "FontSpecific" -> 178 | S; 179 | _ -> 180 | "MacRomanEncoding" 181 | end. 182 | 183 | mkFont1(M, FontDescriptorPrt, Index) -> 184 | FirstChar = M:firstChar(), 185 | LastChar = M:lastChar(), 186 | Widths = make_width(M:encoding(), M, FirstChar, LastChar), 187 | {dict,[{"Type",{name,"Font"}}, 188 | {"Subtype",{name,"Type1"}}, 189 | {"Name",{name,"F" ++ eg_pdf_op:i2s(Index)}}, 190 | {"BaseFont",{name,M:fontName()}}, 191 | {"Encoding",{name,encoding(M)}}, 192 | {"FirstChar",FirstChar}, 193 | {"LastChar",LastChar}, 194 | {"Widths", {array,Widths}}, 195 | {"FontDescriptor",{ptr,FontDescriptorPrt,0}}]}. 196 | 197 | make_width("AdobeStandardEncoding", M, F, L) -> 198 | Seq = lists:seq(F,L), 199 | Fu = fun(unknown) -> 0; 200 | (X) -> X 201 | end, 202 | Map = eg_convert:mac2pdf(Seq), 203 | [Fu(M:width(X)) || X <- Map]; 204 | make_width(_, M, _, _) -> 205 | M:widths(). 206 | 207 | mkFontDescriptor(M, Embedded, I) -> 208 | {X1,X2,X3,_X4} = M:fontBBox(), 209 | %% io:format("Flags FIXED to 6 ...~n"), 210 | FontBBox = [X1,X2,X3,X3], 211 | D0 = [{"Type",{name,"FontDescriptor"}}, 212 | {"Ascent", M:ascender()}, 213 | {"CapHeight", M:capHeight()}, 214 | {"Descent", M:descender()}, 215 | {"Flags", M:flags()}, 216 | {"FontBBox",{array,FontBBox}}, 217 | {"FontName",{name,M:fontName()}}, 218 | {"ItalicAngle",M:italicAngle()}, 219 | {"StemV",M:stemV()}, 220 | {"XHeight",M:xHeight()}], 221 | D = case Embedded of 222 | true -> 223 | [{"FontFile", {ptr,I,0}}|D0]; 224 | false -> 225 | D0 226 | end, 227 | {dict, D}. 228 | 229 | %% {{obj,8,0}, 230 | %% {dict,[{"Type",{name,"FontDescriptor"}}, 231 | %% {"Ascent",890}, 232 | %% {"CapHeight",707}, 233 | %% {"Descent",65306}, 234 | %% {"Flags",6}, 235 | %% {"FontBBox",{array,[-100,-65311,1218,895]}}, 236 | %% {"FontName",{name,"UtopiaMedium"}}, 237 | %% {"ItalicAngle",0}, 238 | %% {"StemV",80}, 239 | %% {"XHeight",512}, 240 | %% {"FontFile",{ptr,9,0}}]}}, 241 | %% {{obj,9,0}, 242 | %% {stream,94215, 243 | %% "stream_9_0_94215", 244 | %% [{"Length",94215}, 245 | %% {"Length1",5750}, 246 | %% {"Length2",87922}, 247 | %% {"Length3",543}]}}, 248 | 249 | mkFontFile(Handler) -> 250 | {Len,Len1,Len2,Len3,Bin} = get_font_program(Handler), 251 | {stream,{dict,[{"Length",Len}, 252 | {"Length1",Len1}, 253 | {"Length2",Len2}, 254 | {"Length3",Len3}]}, 255 | Bin}. 256 | 257 | 258 | get_font_program(Handler) -> 259 | File = filename:join(eg_lib:priv_src_dir(), 260 | atom_to_list(Handler) ++ ".pfb"), 261 | %% io:format("reading Font from:~s~n",[File]), 262 | P = eg_embed:parse_pfb(File), 263 | case P of 264 | [{_,L1,B1},{_,L2,B2},{_,L3,B3}|_] -> 265 | {L1+L2+L3,L1,L2,L3,list_to_binary([B1,B2,B3])}; 266 | _ -> 267 | error 268 | end. 269 | -------------------------------------------------------------------------------- /src/eg_pdf_page.erl: -------------------------------------------------------------------------------- 1 | %%========================================================================== 2 | %% Copyright (C) 2004 Mikael Karlsson 3 | %% 4 | %% Permission is hereby granted, free of charge, to any person obtaining a 5 | %% copy of this software and associated documentation files (the 6 | %% "Software"), to deal in the Software without restriction, including 7 | %% without limitation the rights to use, copy, modify, merge, publish, 8 | %% distribute, sublicense, and/or sell copies of the Software, and to permit 9 | %% persons to whom the Software is furnished to do so, subject to the 10 | %% following conditions: 11 | %% 12 | %% The above copyright notice and this permission notice shall be included 13 | %% in all copies or substantial portions of the Software. 14 | %% 15 | %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | %% OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | %% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 18 | %% NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 19 | %% DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 | %% OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 21 | %% USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | %% 23 | %% Author: Mikael Karlsson 24 | %% Purpose: PDF Pages 25 | %%========================================================================== 26 | 27 | %% @doc Page. 28 | %% 29 | %%

Purpose: Create PDF Pages

30 | %%

Ref. Chapter 3.6.2 Page objects in PDF reference v1.4

31 | %% 32 | %%

33 | %%

34 | %%
/Type
/Page
35 | %%
/Parent
dictionary, Required; indirect reference. The parent 36 | %% page tree node.
37 | %%
/LastModified
38 | %%
date, Required if PieceInfo
39 | %%
/Resources
40 | %%
dictionary. Required, inheritable. The resources required by the 41 | %% page.
42 | %%
/MediaBox
rectangle. Required, inheritable. Boundaries 43 | %% of physical medium.
44 | %%
/CropBox
rectangle. Optional, inheritable. Visible region 45 | %% of default user space. Defaults to value of MediaBox
46 | %%
/BleedBox
47 | %%
rectangle, Optional. Clipping region for production environment. 48 | %% Defaults to value of CropBox.
49 | %%
/TrimBox
50 | %%
rectangle, Optional. Intended dimensions of finished page after 51 | %% trimming.
52 | %%
/ArtBox
rectangle, Optional. 53 | %% Intended dimensions of the finished page after trimming.
54 | %%
/BoxColorInfo
55 | %%
dictionary, Optional. Se PDF reference
56 | %%
/Contents
stream or array. Content stream.
57 | %%
/Rotate
58 | %%
integer, Optional, inheritable. The number of degrees the page 59 | %% is rotated cloclwise when displayed.
60 | %%
/Group
61 | %%
dictionary, Optional. See PDF reference
62 | %%
/Thumb
stream, Optional. Stream object defining the page's 63 | %% thumbnail image.
64 | %%
/B
array, Optional. Array of indirect references to 65 | %% article beads appearing on the page.
66 | %%
/Dur
number, Optional. Display duration in number of 67 | %% seconds during presentations.
68 | %%
/Trans
dictionary, Optional. Transition dictionary 69 | %% for presentations
70 | %%
/Annots
array, Optional. Array of annotation 71 | %% dictionaries.
72 | %%
/AA
dictionary, Optional. Additional actions dictionary
73 | %%
/Metadata
stream, Optional. Metadata for the page
74 | %%
/PieceInfo
dictionary, Optional. See PDF reference.
75 | %%
/StructParents
integer, Required if the page contains 76 | %% structural content items.
77 | %%
/ID
string, Optional. Digital id of the page parent Web 78 | %% Capture content set.
79 | %%
/PZ
number, Optional. Preferred zoom factor.
80 | %%
/SeparationInfo
dictionary, Optional. Separation dictionary 81 | %% to generate color separations fopr the page.
82 | %%
83 | %%

84 | %% @end 85 | 86 | -module(eg_pdf_page). 87 | 88 | -export([page/3, 89 | append_page/2, 90 | append_page2tree/2, 91 | append_to_page/3, 92 | get_page/2, 93 | get_page_contents/2, 94 | page_tree/5]). 95 | 96 | %% ============================================================================ 97 | 98 | 99 | %% @spec page(ParenRef::integer(), ContentRef::integer(), Options) -> dict() 100 | %% Options = [Option] 101 | %% Option = {'"LastModified"', date()} | {'"Resources"', dict() } | 102 | %% {'"Annots"', array() } | {Key, pdftype()} 103 | page(ParentRef, ContentsRef, Options) -> 104 | {dict, [{"Type", {name,"Page"}}, 105 | {"Parent", {ptr,ParentRef,0}}, 106 | {"Contents", {ptr, ContentsRef, 0}} 107 | | lists:map( fun page_opt/1, Options ) ]}. 108 | 109 | page_opt({"LastModified" = A, {date, Date}}) -> 110 | {A,{date, Date}}; 111 | page_opt({"Resources", {dict,_}} = A) -> 112 | A; 113 | page_opt({"Resources", {ptr,_,_}} = A) -> 114 | A; 115 | page_opt({"Annots", _}=A ) -> 116 | A; 117 | page_opt({A,B}) -> 118 | {A,B}. 119 | 120 | %% @spec page_tree(KidRefs, FontsPtr::ptr(), XObjectsPtr::ptr(), 121 | %% MediaBox::rect(), ProcSet) -> dict() 122 | %% KidRefs = [ number() ] 123 | %% ProcSet = imageb | imagec | imagebc 124 | %% @doc Creates a Pages (Page Tree) dictionary 125 | page_tree(KidRefs, FontsPtr, XObjectsPtr, MediaBox = {rect,{_A,_B,_C,_D}}, 126 | ProcSet ) -> 127 | ImProcSet = case ProcSet of 128 | imagebc -> [{name, "ImageB"},{name, "ImageC"}]; 129 | imageb -> [{name, "ImageB"}]; 130 | imagec -> [{name, "ImageC"}]; 131 | _ -> [] 132 | end, 133 | {dict,[{"Type",{name,"Pages"}}, 134 | {"Count",length(KidRefs)}, 135 | {"MediaBox", MediaBox }, 136 | {"Kids",{array, lists:map(fun(I) ->{ptr,I,0} end, KidRefs)}}, 137 | {"Resources", 138 | {dict,[{"Font", FontsPtr },{"XObject", XObjectsPtr }, 139 | {"ProcSet", {array,[{name,"PDF"},{name,"Text"} | 140 | ImProcSet]}}]}}]}. 141 | 142 | 143 | append_page(PageContents, Objects) -> 144 | [PageTreeObj] = eg_pdf_lib:get_objects_of_type("Pages", Objects), 145 | PTRef = eg_pdf_lib:get_ref(PageTreeObj), 146 | {ContentRef,Objects1} = eg_pdf_lib:add_object({stream, PageContents}, 147 | Objects), 148 | {PageRef, Objects2} = eg_pdf_lib:add_object(page(PTRef,ContentRef,[]), 149 | Objects1), 150 | NewPTreeItem = append_page2tree(PageRef, eg_pdf_lib:pdf_item(PageTreeObj)), 151 | Objects3 = eg_pdf_lib:store_object( 152 | eg_pdf_lib:make_object(PTRef, NewPTreeItem), Objects2), 153 | Objects3. 154 | 155 | 156 | append_page2tree(Ref, PageTreeItem) -> 157 | Key = "Kids", 158 | {array, Kids} = eg_pdf_lib:find_in_dict(Key, PageTreeItem), 159 | NewKids = Kids ++ [{ptr, Ref, 0}], 160 | eg_pdf_lib:store_in_dict({Key, {array, NewKids}}, PageTreeItem). 161 | 162 | get_page(PageNo, Objects) -> 163 | [PageTreeObj] = eg_pdf_lib:get_objects_of_type("Pages", Objects), 164 | PageTree = eg_pdf_lib:pdf_item(PageTreeObj), 165 | {array, Kids} = eg_pdf_lib:find_in_dict("Kids", PageTree), 166 | PagePtr = lists:nth(PageNo, Kids), 167 | case eg_pdf_lib:search_object(PagePtr, Objects) of 168 | {value, Object} -> 169 | Object; 170 | false -> 171 | no_page_available 172 | end. 173 | 174 | get_page_contents(PageObj, Objects) -> 175 | PageDict = eg_pdf_lib:pdf_item(PageObj), 176 | case eg_pdf_lib:find_in_dict("Contents", PageDict) of 177 | {ptr, _I, _J} = Ptr -> 178 | {value, Object} = eg_pdf_lib:search_object(Ptr, Objects), 179 | Object; 180 | PtrList -> 181 | lists:map(fun(Ptr) -> 182 | {value, Object} = eg_pdf_lib:search_object(Ptr, Objects), 183 | Object 184 | end, PtrList) 185 | end. 186 | 187 | append_to_page(S, PageObj, Objects) -> 188 | {Key, {stream, Str}} = get_page_contents(PageObj, Objects), 189 | eg_pdf_lib:store_object({Key, {stream, Str ++ S}}, Objects). 190 | 191 | 192 | 193 | -------------------------------------------------------------------------------- /src/eg_richText.erl: -------------------------------------------------------------------------------- 1 | %%========================================================================== 2 | %% Permission is hereby granted, free of charge, to any person obtaining a 3 | %% copy of this software and associated documentation files (the 4 | %% "Software"), to deal in the Software without restriction, including 5 | %% without limitation the rights to use, copy, modify, merge, publish, 6 | %% distribute, sublicense, and/or sell copies of the Software, and to permit 7 | %% persons to whom the Software is furnished to do so, subject to the 8 | %% following conditions: 9 | %% 10 | %% The above copyright notice and this permission notice shall be included 11 | %% in all copies or substantial portions of the Software. 12 | %% 13 | %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 | %% OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 15 | %% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 16 | %% NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | %% DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 18 | %% OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 19 | %% USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | %%========================================================================== 21 | 22 | -module(eg_richText). 23 | 24 | %% OLD AND BROKEN doc ?! (see modern version further down) 25 | 26 | %% encapsulates operations on text() and inline() objects 27 | 28 | %% ADT 29 | %% +deftype richText() = {richText, [inLine()] 30 | %% +deftype inLine() = Word | FixedString | Opaque | Space | NL 31 | %% +deftype Word = {word, Width, Face, Str}, 32 | %% +deftype FixedStr = {fixedStr, Width, Face, Str}, 33 | %% +deftype Opaque = {opaque, Width, X} 34 | %% +deftype Space = {space, Width, Face} 35 | %% +deftype NL = {nl, Face} 36 | %% +deftype Face = {Font, PointSize, Voffset, Color, Breakable} 37 | %% +deftype Color = default | {R,G,B} 38 | 39 | %% Interface 40 | %% str2text(Foint, PointSize, Str) -> text() 41 | %% text2str(text()) -> str(). 42 | 43 | %% ---------------------------------------------------------------------------- 44 | %% This is what str2richText/6 returns, above ADT and Interface appears to be 45 | %% out of date. 46 | %% 47 | %% @type richText() = {richText, [inLine()]} 48 | %% @type inLine() = word() | space() | nl() 49 | %% @type word() = {word, Width::thousendth_pt(), Face::record(face), 50 | %% Str::string()} 51 | %% @type space() = {space, Width::thousendth_pt(), Face::record(face)} 52 | %% @type nl() = {nl, Face::record(face)} 53 | %% @type thousendth_pt() = integer() 54 | %% @end 55 | %% ---------------------------------------------------------------------------- 56 | 57 | -export([test/1, 58 | cloan_space/1, 59 | cloan_word/2, 60 | font/1, 61 | fontFromFace/1, 62 | classify_inline/1, 63 | widthExcludingSpaces/1, 64 | pointSize/1, 65 | width/1, 66 | color/1, 67 | is_face_breakable/1, 68 | is_breakable/1, 69 | is_space/1, 70 | is_nl/1, 71 | is_word/1, 72 | lineWidth/1, 73 | numberOfSpaces/1, 74 | mk_test_word/1, 75 | mk_face/5, 76 | mk_fixedStr/2, 77 | mk_word/2, 78 | mk_nl/1, 79 | mk_space/1, 80 | string/1, 81 | str2richText/1, 82 | str2richText/2, 83 | str2richText/6, 84 | richText2str/1, 85 | width_of/3 86 | ]). 87 | 88 | 89 | -include("eg.hrl"). 90 | 91 | 92 | %% -define(DEBUG, true). 93 | 94 | -ifdef(DEBUG). 95 | dbg_io(Str) -> dbg_io(Str,[]). 96 | dbg_io(Str,Args) -> 97 | io:format("eg_richText: ~p " ++ Str, [self()] ++ Args), 98 | ok. 99 | -else. 100 | dbg_io(_) -> ok. 101 | dbg_io(_,_) -> ok. 102 | -endif. 103 | 104 | 105 | test(1) -> 106 | str2richText("TimesDutch", 12, 0, default, true, 107 | "Hello joe how are you today? 108 | May I take this opportunity 109 | of saying 110 | that my favorite color is blue. 111 | Have a nice day, 112 | from Mr. C. Computer."). 113 | 114 | richText2str({richText, L}) -> 115 | eg_pdf_op:flatten(lists:map(fun inline2str/1, L)). 116 | 117 | inline2str({word,_,_Face,Str}) -> Str; 118 | inline2str({space,_, _}) -> " "; 119 | inline2str({nl,_}) -> "\n"; 120 | inline2str({fixedStr,_,_Face,Str}) -> Str; 121 | inline2str(_) -> "". 122 | 123 | %% ---------------------------------------------------------------------------- 124 | str2richText(Str) -> 125 | str2richText("Times-Roman", 12, 0, default, true, Str). 126 | 127 | str2richText(Str, Pts) -> 128 | str2richText("Times-Roman", Pts, 0, default, true, Str). 129 | 130 | %% ---------------------------------------------------------------------------- 131 | %% @spec str2richText(Font, Point, Voff, Color, Break, Str) -> richText() 132 | %% @doc convert string to "word" sequence containing word font/size info 133 | %% @end------------------------------------------------------------------------ 134 | str2richText(Font, Point, Voff, Color, Break, Str) -> 135 | valid_bool(Break), 136 | F = fontHandler(Font), 137 | Face = #face{font=F, pointSize=Point, vOffset=Voff, 138 | color=Color, breakable=Break}, 139 | L1 = normalise_str(Str, []), 140 | L2 = lists:map(fun({wd1,S}) -> 141 | Width = width_of(F, Point, S), 142 | {word, Width, Face, S}; 143 | (spaces) -> 144 | Width = width_of(F, Point, [$\s]), 145 | {space, Width, Face}; 146 | (lineFeed) -> 147 | {nl, Face} 148 | end, L1), 149 | {richText, L2}. 150 | 151 | valid_bool(true) -> true; 152 | valid_bool(false) -> true; 153 | valid_bool(X) -> exit({badarg, str2richText, breakNotBool, was, X}). 154 | 155 | %% ---------------------------------------------------------------------------- 156 | %% @spec normalise_str(Str, Acc) -> [spaces | lineFeed | {wd1, Word::string()}] 157 | %% @doc split Str into word and space segments, single and duplicate 158 | %% whitespace chars get replaced by a single 'spaces', NL -> lineFeed 159 | %% @end------------------------------------------------------------------------ 160 | normalise_str([], L) -> 161 | lists:reverse(L); 162 | normalise_str([$\n|T], L) -> 163 | normalise_str(T, [lineFeed|L]); 164 | normalise_str([H|T], L) -> 165 | case is_white(H) of 166 | true -> 167 | T1 = skip_white(T), 168 | normalise_str(T1, [spaces|L]); 169 | false -> 170 | {Word, T1} = collect_word(T, [H]), 171 | normalise_str(T1, [{wd1,Word}|L]) 172 | end. 173 | 174 | is_white($\s) -> true; 175 | is_white(160) -> true; %% non-break-space 176 | is_white($\t) -> true; 177 | is_white($\r) -> true; 178 | is_white(_) -> false. 179 | 180 | skip_white(X=[H|T]) -> 181 | case is_white(H) of 182 | true -> skip_white(T); 183 | false -> X 184 | end; 185 | skip_white([]) -> 186 | []. 187 | 188 | collect_word(X=[$\n|_T], L) -> 189 | {lists:reverse(L), X}; 190 | collect_word(X=[H|T], L) -> 191 | case is_white(H) of 192 | true -> {lists:reverse(L), X}; 193 | false -> collect_word(T, [H|L]) 194 | end; 195 | collect_word([], L) -> 196 | {lists:reverse(L), []}. 197 | 198 | width_of(Font, PointSize, Str) -> 199 | PointSize * sizeof(Font, Str). 200 | 201 | 202 | %%---------------------------------------------------------------------- 203 | %% sizeof(FontIndex, Str) 204 | %% Computes width of Str which is of type FontIndex 205 | %% Size is correctly adjusted for kerning information 206 | 207 | sizeof(Font, Str) -> 208 | Widths = lists:map(fun(I) -> char_width(Font, I) end, Str), 209 | %% dbg_io("Str=|~s| Widths=~p~nFont=~p~n",[Str, Widths, Font]), 210 | W1 = lists:sum(Widths), 211 | %% and add the correct kerning info 212 | kern_adj(Str, W1, Font). 213 | 214 | char_width(Font, I) -> 215 | case Font:width(I) of 216 | Width when is_integer(Width) -> 217 | Width; 218 | _ -> 219 | dbg_io("Character ~w in font ~p has no width~n", [I, Font]), 220 | Font:width($\s) 221 | end. 222 | 223 | kern_adj([H1,H2|T], W, Font) -> 224 | Extra = Font:kern(H1, H2), 225 | kern_adj([H2|T], W+Extra, Font); 226 | kern_adj(_, W, _) -> 227 | W. 228 | 229 | %%---------------------------------------------------------------------- 230 | %% access funtions 231 | 232 | is_space(X) -> element(1,X) == space. 233 | 234 | is_word(X) -> element(1, X) == word. 235 | 236 | is_nl(X) -> element(1, X) == nl. 237 | 238 | is_breakable({word,_,Face,_Str}) -> Face#face.breakable; 239 | is_breakable(_) -> false. 240 | 241 | %% Make a new word based on the face of an old word 242 | 243 | cloan_word({word,_,Face,_}, Str) -> 244 | Font = Face#face.font, 245 | PointSize = Face#face.pointSize, 246 | W = width_of(Font, PointSize, Str), 247 | {word, W, Face, Str}. 248 | 249 | cloan_space({word,_,Face,_}) -> 250 | cloan_space_from_face(Face); 251 | cloan_space({nl, Face}) -> 252 | cloan_space_from_face(Face). 253 | 254 | cloan_space_from_face(Face) -> 255 | Font = Face#face.font, 256 | PointSize = Face#face.pointSize, 257 | W = width_of(Font, PointSize, [$\s]), 258 | {space, W, Face}. 259 | 260 | width({word,W,_,_}) -> W; 261 | width({opaque,W,_}) -> W; 262 | width({space,W,_}) -> W; 263 | width({nl,_}) -> 0; 264 | width({fixedStr,W,_,_}) -> W. 265 | 266 | font({word,_,F,_}) -> F#face.font; 267 | font({opaque,_,_F}) -> unknown; 268 | font({space,_,F}) -> F#face.font; 269 | font({nl,F}) -> F#face.font; 270 | font({fixedStr,_,F,_}) -> F#face.font. 271 | 272 | fontFromFace(F) -> F#face.font. 273 | 274 | color({word,_,F,_}) -> F#face.color; 275 | color({opaque,_,_F}) -> unknown; 276 | color({space,_,F}) -> F#face.color; 277 | color({nl,F}) -> F#face.color; 278 | color({fixedStr,_,F,_}) -> F#face.color. 279 | 280 | pointSize({word,_,F,_}) -> F#face.pointSize; 281 | pointSize({opaque,_,_F}) -> unknown; 282 | pointSize({space,_,F}) -> F#face.pointSize; 283 | pointSize({nl,F}) -> F#face.pointSize; 284 | pointSize({fixedStr,_,F,_}) -> F#face.pointSize. 285 | 286 | classify_inline({word,_W,_,_}) -> word; 287 | classify_inline({opaque,_W,_}) -> opaque; 288 | classify_inline({space,_W,_}) -> space; 289 | classify_inline({nl,_}) -> nl; 290 | classify_inline({fixedStr,_W,_,_}) -> fixedStr. 291 | 292 | string({word,_,_,S}) -> S; 293 | string({fixedStr,_,_,S}) -> S; 294 | string({space,_,_}) -> " ". 295 | 296 | %% +deftype FixedStr = {fixedStr, Width, Face, Str}, 297 | %% +deftype Opaque = {opaque, Width, X} 298 | %% +deftype Space = {space, Width, Face} 299 | %% +deftype NL = {nl, Face} 300 | %% +deftype Face = {Font, PointSize, Voffset, Color, Breakable} 301 | %% +deftype Color = default | {R,G,B} 302 | 303 | mk_word(Face, Str) -> 304 | F = Face#face.font, 305 | P = Face#face.pointSize, 306 | Width = width_of(F, P, Str), 307 | {word, Width, Face, Str}. 308 | 309 | mk_fixedStr(Face, Str) -> 310 | F = Face#face.font, 311 | P = Face#face.pointSize, 312 | Width = width_of(F, P, Str), 313 | {fixedStr, Width, Face, Str}. 314 | 315 | mk_nl(Face) -> 316 | {nl, Face}. 317 | 318 | mk_space(Face) -> 319 | F = Face#face.font, 320 | P = Face#face.pointSize, 321 | Width = P * char_width(F, 32), 322 | {space, Width, Face}. 323 | 324 | %% make a word that we can play with 325 | 326 | mk_test_word(Str) -> 327 | F = fontHandler("Times-Roman"), 328 | Face = #face{font=F, pointSize=16, vOffset=0, 329 | color=default, breakable=true}, 330 | Width = width_of(F, 16, Str), 331 | {word, Width, Face, Str}. 332 | 333 | mk_face(Font, PointSize, Breakable, Color, VoffSet) -> 334 | F = fontHandler(Font), 335 | #face{font=F, pointSize=PointSize, vOffset=VoffSet, 336 | color=Color, breakable=Breakable}. 337 | 338 | fontHandler(Font) -> 339 | case eg_font_map:handler(Font) of 340 | undefined -> 341 | dbg_io("There is no font called:~s~n",[Font]), 342 | dbg_io("Using Times-Roman~n"), 343 | eg_font_map:handler("Times-Roman"); 344 | Mod -> 345 | Mod 346 | end. 347 | 348 | lineWidth(Toks) -> 349 | lists:foldl(fun(I, S) -> width(I) + S end, 0, Toks). 350 | 351 | numberOfSpaces(Toks) -> 352 | Toks1 = lists:filter(fun(I) -> is_space(I) end, Toks), 353 | length(Toks1). 354 | 355 | widthExcludingSpaces(Toks) -> 356 | lists:foldl(fun(I, S) -> 357 | case is_space(I) of 358 | true -> S; 359 | false -> S + width(I) 360 | end 361 | end, 0, Toks). 362 | 363 | is_face_breakable(F) -> 364 | F#face.breakable. 365 | 366 | -------------------------------------------------------------------------------- /src/eg_richText2pdf.erl: -------------------------------------------------------------------------------- 1 | %%========================================================================== 2 | %% Copyright (C) 2003 Joe Armstrong 3 | %% 4 | %% Permission is hereby granted, free of charge, to any person obtaining a 5 | %% copy of this software and associated documentation files (the 6 | %% "Software"), to deal in the Software without restriction, including 7 | %% without limitation the rights to use, copy, modify, merge, publish, 8 | %% distribute, sublicense, and/or sell copies of the Software, and to permit 9 | %% persons to whom the Software is furnished to do so, subject to the 10 | %% following conditions: 11 | %% 12 | %% The above copyright notice and this permission notice shall be included 13 | %% in all copies or substantial portions of the Software. 14 | %% 15 | %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | %% OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | %% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 18 | %% NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 19 | %% DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 | %% OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 21 | %% USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | %% 23 | %% Author: Joe Armstrong 24 | %% Purpose: Convert internal form of line to PDF 25 | %%========================================================================== 26 | 27 | -module(eg_richText2pdf). 28 | 29 | %% There is a bug in this code: Blanks *inside* 30 | %% strings are subject to expansion caused by the Tw operator 31 | 32 | -export([richText2pdf/8]). 33 | 34 | 35 | -record(pdf, {color = default, 36 | xy = {-1,-1}, 37 | face = {none,none}, 38 | tw = -1, 39 | inTJ = false, 40 | code = []}). 41 | 42 | %% -define(DEBUG, true). 43 | 44 | -ifdef(DEBUG). 45 | dbg_io(Str) -> dbg_io(Str,[]). 46 | dbg_io(Str,Args) -> 47 | io:format("eg_richText2pdf: ~p " ++ Str, [self()] ++ Args), 48 | ok. 49 | -else. 50 | %dbg_io(_) -> ok. 51 | dbg_io(_,_) -> ok. 52 | -endif. 53 | 54 | %% ============================================================================ 55 | 56 | %% returns: Code 57 | %% usage : * add pdf content to PDF using eg_pdf:append_stream(PDF, Code) 58 | %% * note that this must be wrapped in eg_pdf:begin_text(PDF) and 59 | %% eg_pdf:end_text(PDF) to create a 'text object' this is required 60 | %% as the usage of Rot adds a 'Tm' (transformation matrix) in the pdf 61 | %% source, which may only be used inside a 'text object' (chp 5.3 62 | %% in pdf reference manual 1.4 and 1.7) 63 | richText2pdf(X, Y0, Type, Rot, Lines, Leading, Widths, Offsets) -> 64 | Y = Y0 - Leading, 65 | P = start(), 66 | P2 = case Type of 67 | justified -> 68 | {_Cos, _Sin, P1} = init_rotation_matrix(X, Y0, Rot, P), 69 | make_justified(X,Y,Leading,Lines,Offsets,Widths,P1); 70 | Style when Style == left_justified; 71 | Style == right_justified; 72 | Style == centered -> 73 | {_Cos, _Sin, P1} = init_rotation_matrix(X, Y0, Rot, P), 74 | make_para(X, Y, Leading, Lines,Offsets,Widths,Style,P1) 75 | end, 76 | finalise(P2). 77 | 78 | make_justified(_X, _Y, _Leading, [], _, _, P) -> P; 79 | make_justified(X, Y, _Leading, [H], [O|_], [W|_], P) -> 80 | line2pdf(X+O,Y,H,W,last_line_justified, P); 81 | make_justified(X, Y, Leading, [H|T], [O|_O1] = O0, [W|_W1] = W0, P) -> 82 | {O2, W2} = last_offset_width(O0, W0), 83 | P1 = line2pdf(X+O,Y,H,W,justified,P), 84 | make_justified(X, Y-Leading, Leading, T, O2, W2, P1). 85 | 86 | make_para(_X, _Y, _Leading, [], _, _, _, P) -> P; 87 | make_para(X, Y, _Leading, [H], [O|_], [W|_], Style, P) -> 88 | line2pdf(X+O,Y,H,W,Style, P); 89 | make_para(X, Y, Leading, [H|T], [O|_O1] = O0, [W|_W1] = W0, Style, P) -> 90 | {O2, W2} = last_offset_width(O0, W0), 91 | P1 = line2pdf(X+O,Y,H,W,Style,P), 92 | make_para(X, Y-Leading, Leading, T, O2, W2, Style, P1). 93 | 94 | last_offset_width([O|O1],[W|W1]) -> 95 | O2 = if O1 == [] -> [O]; 96 | true -> O1 97 | end, 98 | W2 = if W1 == [] -> [W]; 99 | true -> W1 100 | end, 101 | {O2, W2}. 102 | 103 | line2pdf(X, Y, {richText, Line}, Len, Style, P) -> 104 | TotWidth = eg_richText:lineWidth(Line)/1000, 105 | NS = eg_richText:numberOfSpaces(Line), 106 | case Style of 107 | justified -> 108 | Tw = if 109 | NS > 0 -> 110 | Width = (Len - TotWidth)/NS, 111 | Width; 112 | NS == 0 -> 113 | 0 114 | end, 115 | %% dbg_io("Line=~p~n",[Line]), 116 | %% dbg_io("NS=~p Tw=~p Len=~p TotWidth=~p~n", 117 | %% [NS,Tw,Len,TotWidth]), 118 | make_line(X, Y, Line, Tw, P); 119 | last_line_justified -> 120 | %% The last line of a justfied para has to be handled with care 121 | %% It might happen that the line needs to be squashed ... 122 | if 123 | TotWidth > Len -> 124 | line2pdf(X, Y, {richText, Line}, Len, justified, P); 125 | true -> 126 | line2pdf(X, Y, {richText, Line}, Len, left_justified, P) 127 | end; 128 | left_justified -> 129 | make_line(X, Y, Line, 0, P); 130 | right_justified -> 131 | Excess = Len - TotWidth, 132 | make_line(X+Excess, Y, Line, 0, P); 133 | centered -> 134 | %% dbg_io("Len = ~p~n",[Len]), 135 | %% dbg_io("TotWidth = ~p~n",[TotWidth]), 136 | Offset = round(Len - TotWidth) div 2, 137 | 138 | %% dbg_io("Offset = ~p~n",[Offset]), 139 | make_line(X+Offset, Y, Line, 0, P) 140 | end. 141 | 142 | make_line(X, Y, Line, Tw, P) -> 143 | %% dbg_io("make line~p at pos=~p ~p~n",[Line,X,Y]), 144 | P1 = add_move(X, Y, P), 145 | make_line(Line, Tw, P1). 146 | 147 | make_line([H|T], Tw, P) -> 148 | case eg_richText:classify_inline(H) of 149 | space -> 150 | {Font,Size} = get_font_info(H), 151 | P1 = ensure_face(Font, Size, P), 152 | P2 = ensure_tw(Tw, P1), 153 | make_line(T, Tw, add_space(Font, P2)); 154 | word -> 155 | {Font,Size} = get_font_info(H), 156 | Str = eg_richText:string(H), 157 | %% dbg_io("Outputting Word=~p Font=~p oldFont=~p~n", 158 | %% [H,Font,P#pdf.face]), 159 | P1 = ensure_face(Font, Size, P), 160 | P2 = ensure_tw(Tw, P1), 161 | Color = eg_richText:color(H), 162 | P3 = ensure_color(Color, P2), 163 | make_line(T, Tw, add_string(Font, Str, P3)); 164 | fixedStr -> 165 | {Font,Size} = get_font_info(H), 166 | Str = eg_richText:string(H), 167 | P1 = ensure_face(Font, Size, P), 168 | P2 = ensure_tw(0, P1), 169 | make_line(T, Tw, add_string(Font, Str, P2)); 170 | _ -> 171 | dbg_io("Don't know how to make a line with a: ~p~n",[H]), 172 | make_line(T, Tw, P) 173 | end; 174 | make_line([], _, P) -> 175 | P. 176 | 177 | get_font_info(X) -> 178 | {eg_richText:font(X), eg_richText:pointSize(X)}. 179 | 180 | finalise(P) -> 181 | P1 = close_tj(P), 182 | lists:reverse(P1#pdf.code). 183 | 184 | start() -> #pdf{}. 185 | 186 | init_rotation_matrix(X, Y, 0, P) -> 187 | %% dbg_io("here=~p~n",[{X,Y,0,P}]), 188 | C = "1 0 0 1 " ++ eg_pdf_op:n2s(X) ++ " " ++ eg_pdf_op:n2s(Y) ++ " Tm ", 189 | P1 = add_code(C, P#pdf{xy={X,Y}}), 190 | {1,0,P1}; 191 | init_rotation_matrix(X, Y, Rot, P) -> 192 | Rads = 3.14159*Rot/180, 193 | Cos = math:cos(Rads), 194 | Sin = math:sin(Rads), 195 | C = eg_pdf_op:n2s(Cos) ++ " " ++ eg_pdf_op:n2s(Sin) ++ " " ++ 196 | eg_pdf_op:n2s(-Sin) ++ " " ++ eg_pdf_op:n2s(Cos) ++ " " ++ 197 | eg_pdf_op:n2s(X) ++ " " ++ eg_pdf_op:n2s(Y) ++ " Tm ", 198 | P1 = add_code(C, P#pdf{xy={X,Y}}), 199 | {Cos, Sin, P1}. 200 | 201 | add_move(X, Y, P) -> 202 | P1 = close_tj(P), 203 | case P1#pdf.xy of 204 | {-1, -1} -> 205 | %% dbg_io("Here aaa*********~n"), 206 | C = "1 0 0 1 " ++ eg_pdf_op:n2s(X) ++ " " ++ 207 | eg_pdf_op:n2s(Y) ++ " Tm ", 208 | add_code(C, P1#pdf{xy={X,Y}}); 209 | {X, Y} -> 210 | add_code("0 0 TD ", P1); 211 | {OldX, OldY} -> 212 | Dx = X - OldX, 213 | Dy = Y - OldY, 214 | C = eg_pdf_op:n2s(Dx) ++ " " ++ eg_pdf_op:n2s(Dy) ++ " TD ", 215 | add_code(C, P1#pdf{xy={X,Y}}) 216 | end. 217 | 218 | ensure_face(Font, Pts, P) -> 219 | case P#pdf.face of 220 | {Font, Pts} -> 221 | P; 222 | {_, _} -> 223 | P1 = close_tj(P), 224 | P2 = P1#pdf{face={Font,Pts}}, 225 | Index = Font:index(), 226 | add_code("/F" ++ eg_pdf_op:i2s(Index) ++ " " ++ 227 | eg_pdf_op:i2s(Pts) ++ " Tf ", P2) 228 | end. 229 | 230 | ensure_color(Color, P) -> 231 | case P#pdf.color of 232 | Color -> 233 | P; 234 | _ -> 235 | P1 = close_tj(P), 236 | P2 = P1#pdf{color=Color}, 237 | Code = set_color(Color), 238 | add_code(Code, P2) 239 | end. 240 | 241 | 242 | set_color(default) -> 243 | set_color({0,0,0}); 244 | set_color({R,G,B}) -> 245 | eg_pdf_op:f2s(R) ++ " " ++ eg_pdf_op:f2s(G) ++" " ++ 246 | eg_pdf_op:f2s(B) ++ " rg ". 247 | 248 | close_tj(P) -> 249 | case P#pdf.inTJ of 250 | true -> 251 | add_code("] TJ ", P#pdf{inTJ=false}); 252 | false -> 253 | P 254 | end. 255 | 256 | open_tj(P) -> 257 | case P#pdf.inTJ of 258 | false -> 259 | add_code("[", P#pdf{inTJ=true}); 260 | true -> 261 | P 262 | end. 263 | 264 | ensure_tw(N, P) -> 265 | case P#pdf.tw of 266 | N -> P; 267 | _ -> 268 | P1 = close_tj(P), 269 | add_code(eg_pdf_op:n2s(N) ++ " Tw ", P1#pdf{tw=N}) 270 | end. 271 | 272 | add_string(Font, Str, P) -> 273 | %% font etc are set 274 | P1 = open_tj(P), 275 | Code = str2pdf(Font, Str), 276 | add_code(Code, P1). 277 | 278 | add_space(Font, P) -> 279 | add_string(Font, " ", P). 280 | 281 | add_code(Str, P) -> 282 | C1 = P#pdf.code, 283 | P#pdf{code=lists:reverse(Str, C1)}. 284 | 285 | 286 | str2pdf(_Font, "") -> ""; 287 | str2pdf(Font, Str) -> 288 | K = str2TJ(Font, Str), 289 | K1 = lists:map(fun({Str1,Kern}) -> {quote_strings(Str1), Kern} end, K), 290 | Pdf1 = lists:map(fun({S,I}) -> ["(", S,")",eg_pdf_op:i2s(I)] end, K1), 291 | eg_pdf_op:flatten(Pdf1). 292 | 293 | quote_strings([$(|T]) -> [$\\,$(|quote_strings(T)]; 294 | quote_strings([$)|T]) -> [$\\,$)|quote_strings(T)]; 295 | quote_strings([$\\|T]) -> [$\\,$\\|quote_strings(T)]; 296 | %%quote_strings([$"|T]) -> [$\\,$"|quote_strings(T)]; 297 | quote_strings([H|T]) -> [H|quote_strings(T)]; 298 | quote_strings([]) -> []. 299 | 300 | %% str2TJ(Font, Str) -> [{str(),int()}]. 301 | 302 | str2TJ(Font, Str) -> 303 | str2TJ(Font, Str, [], []). 304 | 305 | str2TJ(Font, [32,H|T], Tmp, L) -> 306 | str2TJ(Font, [H|T], [32|Tmp], L); 307 | str2TJ(Font, [H,32|T], Tmp, L) -> 308 | str2TJ(Font, T, [32,H|Tmp], L); 309 | str2TJ(Font, [H1,H2|T], Tmp, L) -> 310 | case Font:kern(H1, H2) of 311 | 0 -> 312 | str2TJ(Font, [H2|T], [H1|Tmp], L); 313 | N -> 314 | Str = lists:reverse([H1|Tmp]), 315 | str2TJ(Font, [H2|T], [], [{Str,-N}|L]) 316 | end; 317 | str2TJ(Font, [H|T], Tmp, L) -> 318 | str2TJ(Font, T, [H|Tmp], L); 319 | str2TJ(_Font, [], [], L) -> 320 | lists:reverse(L); 321 | str2TJ(_Font, [], Tmp, L) -> 322 | lists:reverse([{lists:reverse(Tmp), 0}|L]). 323 | 324 | %% To set the font use /Fn Pt Tf 325 | %% [(A) 90 (W) 120 (A) 105 (Y again - correctly kerned) ] TJ 326 | -------------------------------------------------------------------------------- /src/eg_xml2richText.erl: -------------------------------------------------------------------------------- 1 | %%========================================================================== 2 | %% Copyright (C) 2003 Joe Armstrong 3 | %% 4 | %% Permission is hereby granted, free of charge, to any person obtaining a 5 | %% copy of this software and associated documentation files (the 6 | %% "Software"), to deal in the Software without restriction, including 7 | %% without limitation the rights to use, copy, modify, merge, publish, 8 | %% distribute, sublicense, and/or sell copies of the Software, and to permit 9 | %% persons to whom the Software is furnished to do so, subject to the 10 | %% following conditions: 11 | %% 12 | %% The above copyright notice and this permission notice shall be included 13 | %% in all copies or substantial portions of the Software. 14 | %% 15 | %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | %% OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | %% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 18 | %% NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 19 | %% DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 | %% OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 21 | %% USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | %% 23 | %% Author: Joe Armstrong 24 | %%========================================================================== 25 | 26 | -module(eg_xml2richText). 27 | 28 | -export([normalise_xml/2, 29 | normalise_xml/3, 30 | default_tagMap/1]). 31 | 32 | -include("eg.hrl"). 33 | 34 | %% -define(DEBUG, true). 35 | 36 | -ifdef(DEBUG). 37 | dbg_io(Str) -> dbg_io(Str,[]). 38 | dbg_io(Str,Args) -> 39 | io:format("eg_xml2richText: ~p " ++ Str, [self()] ++ Args), 40 | ok. 41 | -else. 42 | %dbg_io(_) -> ok. 43 | dbg_io(_,_) -> ok. 44 | -endif. 45 | 46 | %%---------------------------------------------------------------------- 47 | %% normalise_xml(XML, RichTextTags, FontMap) -> 48 | %% XML = XML parse tree 49 | %% The tree is walked - if any Tag is in RichTextTags 50 | %% Then the subtree of this tag is assumend to be rich text 51 | %% RichTextTags = [Tag] 52 | %% FontMap = [#face{}] 53 | 54 | %% Invarients no consequative spaces 55 | %% or spaces next to NLs 56 | 57 | default_tagMap(Pts) -> 58 | {[p], 59 | [{default,eg_richText:mk_face("Times-Roman", Pts, true, default, 0)}, 60 | {em, eg_richText:mk_face("Times-Italic", Pts, true, default, 0)}, 61 | 62 | %% XXX !!! the font ZapfChancery-MediumItalic is not availible 63 | {red, eg_richText:mk_face("ZapfChancery-MediumItalic", Pts, true, 64 | {1,0,0},0)}, 65 | {blue, eg_richText:mk_face("ZapfChancery-MediumItalic", Pts, true, 66 | {0,0,1},0)}, 67 | 68 | {code, eg_richText:mk_face("Courier", Pts, false, default, 0)}, 69 | {b, eg_richText:mk_face("Times-Bold", Pts, true, default, 0)} 70 | ]}. 71 | 72 | normalise_xml(XML, {StandardTags, TagMap}) -> 73 | normalise_xml(XML, StandardTags, TagMap). 74 | 75 | normalise_xml({Tag, Args, L}, RichTextTags, TagMap) -> 76 | case lists:member(Tag, RichTextTags) of 77 | true -> 78 | L1 = normalise_richText(L, TagMap), 79 | {Tag, Args, L1}; 80 | false -> 81 | L1 = lists:map(fun(I) -> 82 | normalise_xml(I, RichTextTags, TagMap) 83 | end, L), 84 | {Tag, Args, L1} 85 | end; 86 | normalise_xml(Z, _, _) -> 87 | dbg_io("I cannot normalise:~p~n",[Z]). 88 | 89 | normalise_richText(Items, FontMap) -> 90 | L0 = lists:foldl(fun(I, L0) -> normalise_inline(I, FontMap, L0) end, 91 | [], Items), 92 | L1 = lists:reverse(L0), 93 | test_inline_invarient(L1), 94 | {richText, L1}. 95 | 96 | test_inline_invarient([H1,H2|T]) -> 97 | case {eg_richText:classify_inline(H1), eg_richText:classify_inline(H2)} of 98 | {space, space} -> 99 | dbg_io("Warning spaces:~p ~p~n",[H1,H2]), 100 | test_inline_invarient([H2|T]); 101 | {nl, space} -> 102 | dbg_io("Warning NL + NL:~p ~p~n",[H1,H2]), 103 | test_inline_invarient([H1|T]); 104 | {space,nl} -> 105 | dbg_io("Warning spaces + NL:~p ~p~n",[H1,H2]), 106 | test_inline_invarient([H2|T]); 107 | _ -> 108 | test_inline_invarient([H2|T]) 109 | end; 110 | test_inline_invarient(_) -> 111 | true. 112 | 113 | normalise_inline({raw,Str}, FontMap, L) -> 114 | normalise_tag(default, Str, FontMap, L); 115 | normalise_inline({Tag, _, [{raw,Str}]}, FontMap, L) -> 116 | normalise_tag(Tag, Str, FontMap, L); 117 | normalise_inline({_Tag, _, []}, _FontMap, L) -> 118 | L. 119 | 120 | normalise_tag(Tag, Str, FontMap, L) -> 121 | Face = get_face(Tag, FontMap), 122 | case eg_richText:is_face_breakable(Face) of 123 | true -> 124 | normalise_str(Str, Face, L, skip_ws); 125 | false -> 126 | normalise_str(Str, Face, L, keep_ws) 127 | % Wd = eg_richText:mk_fixedStr(Face, Str), 128 | % [Wd|L] 129 | end. 130 | 131 | get_face(Tag, [{Tag,Face}|_]) -> Face; 132 | get_face(Tag, [_|T]) -> get_face(Tag, T); 133 | get_face(Tag, []) -> 134 | dbg_io("There is no face associated with Tag=~p~n",[Tag]), 135 | eg_pdf:default_face(). 136 | 137 | %% Collect spaces nls etc. 138 | %% in a breakable face 139 | normalise_str([$\r,$\n|T], Face, L, WS) -> 140 | normalise_str(T, Face, [eg_richText:mk_nl(Face)|L], WS); 141 | normalise_str([$\n|T], Face, L, WS) -> 142 | normalise_str(T, Face, [eg_richText:mk_nl(Face)|L], WS); 143 | normalise_str([H|T], Face, L, WS) -> 144 | case {is_white(H), WS} of 145 | {true, skip_ws} -> 146 | %% Hop over the white space 147 | %% If we get to \n put in a NL otherwise 148 | %% put in a space 149 | T1 = skip_white(T), 150 | case T1 of 151 | [] -> 152 | Space = eg_richText:mk_space(Face), 153 | normalise_str(T1, Face, [Space|L], WS); 154 | [H2|_] -> 155 | case is_nl(H2) of 156 | true -> 157 | normalise_str(T1, Face, L, WS); 158 | false -> 159 | Space = eg_richText:mk_space(Face), 160 | normalise_str(T1, Face, [Space|L], WS) 161 | end 162 | end; 163 | {true, keep_ws} -> 164 | Space = eg_richText:mk_space(Face), 165 | normalise_str(T, Face, [Space|L], WS); 166 | {false, _} -> 167 | {Str, T1} = collect_word(T, [H]), 168 | Word = eg_richText:mk_word(Face, Str), 169 | normalise_str(T1, Face, [Word|L], WS) 170 | end; 171 | normalise_str([], _, L, _WS) -> 172 | L. 173 | 174 | %% End Normalise XML 175 | %%---------------------------------------------------------------------- 176 | 177 | %%---------------------------------------------------------------------- 178 | %% misc 179 | 180 | is_white($\s) -> true; 181 | is_white($\t) -> true; 182 | is_white(_) -> false. 183 | 184 | is_white_or_nl($\n) -> true; 185 | is_white_or_nl($\r) -> true; 186 | is_white_or_nl(X) -> is_white(X). 187 | 188 | is_nl($\n) -> true; 189 | is_nl($\r) -> true; 190 | is_nl(_) -> false. 191 | 192 | skip_white(X=[H|T]) -> 193 | case is_white(H) of 194 | true -> skip_white(T); 195 | false -> X 196 | end; 197 | skip_white([]) -> 198 | []. 199 | 200 | collect_word(X=[H|T], L) -> 201 | case is_white_or_nl(H) of 202 | true -> {lists:reverse(L), X}; 203 | false -> collect_word(T, [H|L]) 204 | end; 205 | collect_word([], L) -> 206 | {lists:reverse(L), []}. 207 | 208 | -------------------------------------------------------------------------------- /src/eg_xml_lite.erl: -------------------------------------------------------------------------------- 1 | %%========================================================================== 2 | %% Copyright (C) 2003 Joe Armstrong 3 | %% 4 | %% Permission is hereby granted, free of charge, to any person obtaining a 5 | %% copy of this software and associated documentation files (the 6 | %% "Software"), to deal in the Software without restriction, including 7 | %% without limitation the rights to use, copy, modify, merge, publish, 8 | %% distribute, sublicense, and/or sell copies of the Software, and to permit 9 | %% persons to whom the Software is furnished to do so, subject to the 10 | %% following conditions: 11 | %% 12 | %% The above copyright notice and this permission notice shall be included 13 | %% in all copies or substantial portions of the Software. 14 | %% 15 | %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | %% OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | %% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 18 | %% NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 19 | %% DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 | %% OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 21 | %% USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | %% 23 | %% Authors: Joe Armstrong 24 | %% Purpose: XML parser 25 | %%========================================================================== 26 | 27 | -module(eg_xml_lite). 28 | 29 | -export([parse_all_forms/1, 30 | parse_all_forms/2, 31 | parse_single_form/2, 32 | parse_file/1, 33 | continue/2, 34 | pp/1, 35 | xml2bin/2, 36 | bin2xml/2, 37 | test/1 38 | ]). 39 | 40 | 41 | %% ============================================================================ 42 | 43 | %% Test cases 44 | 45 | test(1) -> 46 | parse_all_forms("aaaaa"); 47 | test(2) -> 48 | {more, C} = parse_single_form("<", 0), 49 | continue(C, "abc>"); 50 | test(3) -> 51 | reent_test("aaaaa" 52 | "ggaa"); 53 | test(4) -> 54 | parse_all_forms("bc"); 55 | test(5) -> 56 | parse_all_forms(" 57 | 58 | 59 |
a
"); 60 | test(6) -> 61 | parse_all_forms("

aaa
aaa

"); 62 | test(7) -> 63 | parse_all_forms("

aaaa aa

"); 65 | test(8) -> 66 | parse_all_forms(" 67 | 68 | 69 |

aaa

"). 70 | 71 | %% This is a reentrant parser for XML streams 72 | 73 | %% +deftype parse_result() = {ok, xmlParseTree(), RestString : string()} 74 | %% | {more, cont()} 75 | %% | {error, parse_error()} 76 | %% +type start(Str) -> parse_result(). 77 | %% +type more(Str, Cont) -> parse_result(). 78 | %% +type format_error(parser_error()) -> string(). 79 | %% +type start_cont() -> cont(). 80 | 81 | %% parse_file(File) -> {error, What} | [Forms] 82 | 83 | parse_file(F) -> 84 | case file:read_file(F) of 85 | {ok, Bin} -> 86 | Result = parse_all_forms(binary_to_list(Bin), 1), 87 | %% case Result of 88 | %% {error, E} -> true; 89 | %% Tree -> pp(Tree) 90 | %% end, 91 | Result; 92 | Error -> 93 | Error 94 | end. 95 | 96 | xml2bin(In, Out) -> 97 | case file:read_file(In) of 98 | {ok, Bin} -> 99 | case parse_all_forms(binary_to_list(Bin), 0) of 100 | {ok, Tree, _} -> 101 | file:write_file(Out, term_to_binary(Tree)); 102 | E = {error, _X} -> 103 | E; 104 | {more, _} -> 105 | {error, incomplete} 106 | end; 107 | Error -> 108 | Error 109 | end. 110 | 111 | bin2xml(In, Out) -> 112 | case file:read_file(In) of 113 | {ok, Bin} -> 114 | Tree = binary_to_term(Bin), 115 | file:write_file(Out, pp(Tree)); 116 | Error -> 117 | Error 118 | end. 119 | 120 | atomize(A={Atom,_}) when is_atom(Atom) -> 121 | A; 122 | atomize({Str,Args,List}) -> 123 | {list_to_atom(Str), Args, lists:map(fun atomize/1, List)}. 124 | 125 | %%---------------------------------------------------------------------- 126 | 127 | %% Top level ... 128 | 129 | parse_all_forms(Str) -> parse_all_forms(Str, 0). 130 | 131 | parse_all_forms(Str, Line) -> top_parse_loop(Str, Line, []). 132 | 133 | top_parse_loop(Str, Line, L) -> 134 | case parse_single_form(Str, Line) of 135 | {ok, Form, Str1, Line1} -> 136 | case all_blanks(Str1) of 137 | true -> 138 | lists:reverse([Form|L]); 139 | false -> 140 | top_parse_loop(Str1, Line1, [Form|L]) 141 | end; 142 | E={error, _Why} -> 143 | E; 144 | {more, _Cont} -> 145 | {error, more_data_expected} 146 | end. 147 | 148 | parse_single_form(Str, Line) -> 149 | parse([], Str, Line). 150 | 151 | continue(Cont, Str) -> 152 | Cont(Str). 153 | 154 | parse(State, Str, Line) -> 155 | tokenise_result(eg_xml_tokenise:get_next_token(Str, Line), State). 156 | 157 | parse_cont(State, Cont, Str) -> 158 | tokenise_result(eg_xml_tokenise:continue(Cont, Str), State). 159 | 160 | tokenise_result({error, Line, What}, _State) -> 161 | {error,{errorInLine,Line,What}}; 162 | tokenise_result({done, Token, Str1, Line1}, State) -> 163 | %% io:format("Token= ~p Str1=~p Line1=~p~n",[Token, Str1, Line1]), 164 | case step_parser(State, Token) of 165 | {more, State1} -> 166 | parse(State1, Str1, Line1); 167 | {done, Parse} -> 168 | {ok, Parse, Str1, Line1}; 169 | {error, What} -> 170 | {error, {errorInLine, Line1,What}} 171 | end; 172 | tokenise_result({more, Cont}, State) -> 173 | {more, fun(Str) -> parse_cont(State, Cont, Str) end}. 174 | 175 | %% The Stack is just [{STag,Args,Collected}] 176 | %% pcdata and completed frames are just pushed onto the stack 177 | %% When an end tag is found it is compared with the start tag 178 | %% if it matches the stack frame is popped and it is 179 | %% merged into the previous stack frame 180 | 181 | %% step_parser(State, Event) -> {more, State1} | {done, Parse} | {error, What} 182 | 183 | step_parser(Stack, {sTag, _, Tag, Args}) -> 184 | %% Push new frame onto the stack 185 | {more, [{Tag, lists:sort(Args), []}|Stack]}; 186 | step_parser([{Tag,Args,C}|L], _P={Flat, _, D}) when Flat == pi; 187 | Flat == raw; 188 | Flat == cdata; 189 | Flat == comment; 190 | Flat == doctype -> 191 | {more, [{Tag,lists:sort(Args),[{Flat,D}|C]}|L]}; 192 | step_parser([{Tag,Args,C}|L], {empty, _, TagE, ArgsE}) -> 193 | {more, [{Tag,Args,[{TagE,lists:sort(ArgsE),[]}|C]}|L]}; 194 | step_parser([{Tag, Args, C}|L], {eTag, _, Tag}) -> 195 | %% This is a matching endtag 196 | %% Now we normalise the arguments that were found 197 | C1 = deblank(lists:reverse(C)), 198 | pfinish([{Tag,Args,C1}|L]); 199 | step_parser([{STag, _Args, _C}|_L], {eTag, _, Tag}) -> 200 | {error,{badendtagfound,Tag,starttagis,STag}}; 201 | step_parser([], {raw, _, S}) -> 202 | case all_blanks(S) of 203 | true -> 204 | {more, []}; 205 | false -> 206 | {error, {nonblank_data_found_before_first_tag, S}} 207 | end; 208 | step_parser([], {Tag,_,D}) when Tag==comment; Tag==doctype; Tag==pi -> 209 | {done, {Tag, D}}; 210 | step_parser(S, I) -> 211 | io:format("UUgh:Stack=~p Item=~p~n",[S, I]). 212 | 213 | 214 | pfinish([X]) -> {done, {xml, atomize(X)}}; 215 | pfinish([H1,{Tag,Args,L}|T]) -> {more, [{Tag,Args,[H1|L]}|T]}. 216 | 217 | deblank(S=[{raw, _C}]) -> S; 218 | deblank(X) -> deblank1(X). 219 | 220 | deblank1([H={raw,X}|T]) -> 221 | case all_blanks(X) of 222 | true -> deblank1(T); 223 | false -> [H|deblank1(T)] 224 | end; 225 | deblank1([H|T]) -> 226 | [H|deblank1(T)]; 227 | deblank1([]) -> 228 | []. 229 | 230 | all_blanks(L) -> lists:all(fun is_Blank/1, L). 231 | 232 | is_Blank($ ) -> true; 233 | is_Blank($\n) -> true; 234 | is_Blank($\t) -> true; 235 | is_Blank($\r) -> true; 236 | is_Blank(_) -> false. 237 | 238 | 239 | %% Pretty printer 240 | 241 | pp(Tree) -> 242 | pp(Tree, 0). 243 | 244 | pp({Node,Args,[{raw,Str}]}, Level) -> 245 | S = name(Node), 246 | [indent(Level),"<",S,pp_args(Args),">",Str,"\n"]; 247 | pp({Node,Args,[]}, Level) -> 248 | S = name(Node), 249 | [indent(Level),"<",S,pp_args(Args),">\n"]; 250 | pp({Node,Args,L}, Level) -> 251 | S = name(Node), 252 | [indent(Level),"<",S,pp_args(Args),">\n", 253 | lists:map(fun(I) -> pp(I, Level+2) end, L), 254 | indent(Level),"\n"]; 255 | pp({raw,Str}, Level) -> 256 | [indent(Level),Str,"/n"]; 257 | pp(X, _Level) -> 258 | io:format("How do I pp:~p~n",[X]), 259 | ["oops"]. 260 | 261 | pp_args([]) -> []; 262 | pp_args([{Key,Val}|T]) -> 263 | Q=quote(Val), 264 | [" ",name(Key),"=",Q,Val,Q|pp_args(T)]. 265 | 266 | quote(Str) -> 267 | case lists:member($", Str) of 268 | true -> $'; 269 | false -> $" 270 | end. 271 | 272 | name(X) -> 273 | atom_to_list(X). 274 | 275 | indent(0) -> []; 276 | indent(N) -> [$ |indent(N-1)]. 277 | 278 | reent_test(_O)->a. 279 | 280 | -------------------------------------------------------------------------------- /src/erlguten.app.src: -------------------------------------------------------------------------------- 1 | %% -*- mode: Erlang; fill-column: 75; comment-column: 50; -*- 2 | 3 | {application, erlguten, 4 | [{description, "System for high-quality typesetting"}, 5 | {vsn, "%VSN%"}, 6 | {modules, ["%MODULES%"]}, 7 | {registered, []}, 8 | {applications, [kernel, stdlib]}]}. 9 | -------------------------------------------------------------------------------- /src/erlguten.erl: -------------------------------------------------------------------------------- 1 | %%========================================================================== 2 | %% Copyright (C) 2003 Joe Armstrong 3 | %% 4 | %% Permission is hereby granted, free of charge, to any person obtaining a 5 | %% copy of this software and associated documentation files (the 6 | %% "Software"), to deal in the Software without restriction, including 7 | %% without limitation the rights to use, copy, modify, merge, publish, 8 | %% distribute, sublicense, and/or sell copies of the Software, and to permit 9 | %% persons to whom the Software is furnished to do so, subject to the 10 | %% following conditions: 11 | %% 12 | %% The above copyright notice and this permission notice shall be included 13 | %% in all copies or substantial portions of the Software. 14 | %% 15 | %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | %% OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | %% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 18 | %% NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 19 | %% DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 | %% OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 21 | %% USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | %% 23 | %% Authors: Joe Armstrong 24 | %% Purpose: Main program 25 | %%========================================================================== 26 | 27 | -module(erlguten). 28 | 29 | -export([batch/1, get_tag_schema/2, parse_flow/1]). 30 | 31 | -export([test/0,bug/0]). 32 | 33 | -import(lists, [map/2]). 34 | 35 | -include("eg.hrl"). 36 | 37 | test() -> format("test1.map"). 38 | 39 | bug() -> format("test2.map"). 40 | 41 | 42 | batch([X]) -> 43 | format(atom_to_list(X)). 44 | 45 | %% A map file has the following structure 46 | %% 47 | %% 56 | %% 57 | %% Logic: 58 | %% in Env 59 | %% set template=template1 60 | %% set current page=1 61 | %% enter intro 62 | %% set currentBox = intro 63 | %% set dict entry {initialised, Page, one} 64 | %% set dict entry {free, Page, intro} = 1 65 | %% set tagMap for template1:tagMap(one, intro) 66 | %% call template1:handler(p, Args, Data, Env) -> Env1 67 | %% call template2:handler(q, Args, Data, Env1) -> Env2 68 | %% etc. 69 | 70 | format(File) -> 71 | V = eg_xml_lite:parse_file(File), 72 | io:format("read:~p~n",[V]), 73 | Out = filename:rootname(File) ++ ".pdf", 74 | case V of 75 | {error, _W} -> 76 | io:format("Error in source(~s):~p~n",[File, V]), 77 | exit(1); 78 | [{pi,_},{xml,{data, [{"page", N}], Templates}}] -> 79 | Page = list_to_integer(N), 80 | io:format("Data starts on page:~p~n",[Page]), 81 | PDF = eg_pdf:new(), 82 | Env = #env{page=Page, pdf=PDF, dict=dict:new()}, 83 | loop(Templates, Env), 84 | {Serialised, _PageNo} = eg_pdf:export(PDF), 85 | file:write_file(Out,[Serialised]), 86 | io:format("Created a file called:~p~n",[Out]), 87 | eg_pdf:delete(PDF); 88 | _ -> 89 | io:format("bad XML - must begin \" 94 | io:format("tempate Args=~p~n",[Args]), 95 | Template= get_template_name(Args), 96 | io:format("Template:~p~n",[Template]), 97 | Env1 = Env#env{template=Template}, 98 | io:format("tempate data=~p~n",[Data]), 99 | Env2 = instanciate_template(Template, Env1), 100 | Env3 = format_boxes(Data, Env2), 101 | loop(T, Env3); 102 | loop([], Env) -> 103 | Env. 104 | 105 | format_boxes([{Box,_Args,Data}|T], Env) -> 106 | Env1 = initialise_box(Box, Env), 107 | %% loop over the paragraphs in the Box 108 | Env2 = format_paragraphs(Data, Box, Env1), 109 | format_boxes(T, Env2); 110 | format_boxes([], E) -> 111 | E. 112 | 113 | initialise_box(Box, E) -> 114 | #env{dict=Dict, page=Page, pdf=PDF, template=Template}=E, 115 | Dict1 = dict:store({free,Page,Box}, 1, Dict), 116 | B = Template:box(Box), 117 | #box{x=XX,y=YY,leading=Lead, width=Width, lines=Lines} = B, 118 | eg_pdf_lib:draw_box(PDF, XX, YY,Width, Lead,Lines), 119 | Env1 = E#env{dict=Dict1, currentBox=Box}, 120 | %% initialse the tagMap 121 | initialise_tagMap(Template, Box, Env1). 122 | 123 | format_paragraphs([{ParaTag,Args,Data}|T], Box, Env) -> 124 | Template = Env#env.template, 125 | case (catch Template:handler(Box, ParaTag, Args, Data, Env)) of 126 | {'EXIT', Why} -> 127 | io:format("oops ~w: ~w ~w Args=~p Data=~p~n",[Template,Box, 128 | ParaTag, 129 | Args,Data]), 130 | io:format("Why=~p~n",[Why]), 131 | exit(1); 132 | Env1 -> 133 | format_paragraphs(T, Box, Env1) 134 | end; 135 | format_paragraphs([], _Box, E) -> 136 | E. 137 | 138 | initialise_tagMap(Template, Box, E) -> 139 | Ts = case (catch Template:tagMap(Box)) of 140 | {'EXIT', _Why} -> 141 | io:format("error in tagmap for ~p:~p~n", 142 | [Template,Box]), 143 | io:format("using defualt~n"), 144 | default_tagmap(); 145 | L -> L 146 | end, 147 | PDF = E#env.pdf, 148 | TagMap = map(fun(I) -> 149 | io:format("Tagmap entry=~p~n",[I]), 150 | eg_pdf:ensure_font_gets_loaded(PDF, I#tagMap.font), 151 | #tagMap{font=F, size=Psize, color=Color, 152 | voff=V, break=Break, name=N} = I, 153 | {N, eg_richText:mk_face(F, Psize, Break, Color, V)} 154 | end, Ts), 155 | E#env{tagMap=TagMap}. 156 | 157 | 158 | default_tagmap() -> 159 | [#tagMap{name=defult,font="Times-Roman",size=11}, 160 | #tagMap{name=em,font="Times-Italic", size=11}, 161 | #tagMap{name=code,font="Courier",size=11,break=false}]. 162 | 163 | instanciate_template(Template, E) -> 164 | #env{dict=Dict,page=Page,pdf=PDF} = E, 165 | _E1 = case dict:find(Key={initialised, Page, Template}, Dict) of 166 | error -> 167 | io:format("calling first instanciation Page:~p " 168 | "Template: ~p ~n", [Page, Template]), 169 | Template:on_instanciation(Page, PDF), 170 | Dict1 = dict:store(Key, true, Dict), 171 | E#env{dict=Dict1}; 172 | _ -> 173 | E 174 | end. 175 | 176 | get_tag_schema(Tag, [H|T]) -> 177 | case H of 178 | Tag -> H; 179 | _ -> get_tag_schema(Tag, T) 180 | end; 181 | get_tag_schema(Tag, []) -> 182 | exit({missing,tag,Tag}). 183 | 184 | parse_flow([{"galley",F},{"name",_Tag}]) -> 185 | case eg_xml_lite:parse_file(F) of 186 | {error, E} -> 187 | io:format("Error in galley(~p):~p~n",[F, E]), 188 | exit(1); 189 | _L -> 190 | %G = parse_galley(F, L), 191 | %get_box(Tag, G) 192 | true 193 | end. 194 | 195 | get_template_name([{"name", N}]) -> 196 | list_to_atom(N). 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | *.beam 2 | eg_test*.pdf 3 | kd_test*.pdf 4 | tmo_doc.pdf 5 | -------------------------------------------------------------------------------- /test/content1.con: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 |

ErlGuten. is a system for hiqh quality typesetting, 7 | ErlGuten is free software. ErlGuten aims to produce typographic 8 | quality PDF directly from XML or from a program.

9 | 10 |

The aim of ErlGuten is to produce high quality PDF from a layout 11 | language or from a program. The ErlGuten distribution includes a 12 | programming API, so that Erlang programs can produce PDF - and a 13 | typesetting system for typesetting documents written in XML.

14 | 15 |

The document that you are reading was authored in XML and typeset 16 | by ErlGuten. The name ErlGuten is chosen because the program is 17 | written in Erlang - the Guten part is a reference to 18 | Gutenberg the father of printing.

19 | 20 |

ErlGuten is a system for high quality typesetting, so we take a 21 | great deal of care when formatting text, a large number of 22 | optimizations are performed which improve the quality of the printed 23 | text. Many of these optimizations are usually only found in expensive 24 | professional type-setting programs. We believe that WYSIWYG programs 25 | have destroyed the fine art of typesetting - ErlGuten is a modest 26 | attempt to improve the situation.

27 | 28 |

We have chosen XML as the input language for it's wide appeal. 29 | XML provides only a thin abstraction layer over the typesetting system 30 | - so the adventurous can use the programming interface to ErlGuten - 31 | to directly produce typographic quality PDF in real-time. We expect 32 | this facility to be useful for the dynamic generation of documents 33 | from web-servers.

34 | 35 |

In ErlGuten we take the view that the highest level of 36 | abstraction is the layout of a document - we are very 37 | concerned that the user can specify the exact position of 38 | text on the printed page. At the next level of abstraction we are 39 | concerned with the typefaces that are used to format different regions 40 | of the document.

41 | 42 |

ErlGuten is designed for the production of large and complex 43 | documents with complex layout requirements, like newspapers 44 | or books. In ErlGuten layout, content, and document 45 | management are considered separate issues. Layout is template 46 | based - Content is assumed to be stored as a large number of 47 | documents in a file system or data base, document management is 48 | considered as a mapping operation which takes documents in the 49 | content data base and maps them onto templates to produce hight 50 | quality output.

51 | 52 |

This is normal text, set 30 picas wide in 12/14 Times Roman. 53 | Many different typefaces can be used within a paragraph. 54 | Emphasized text is set in Times-Italic. Hyphenation uses the 55 | TeX hyphenation algorithm. Any of the 35 built-in PDF typefaces with 56 | the same point size can be mixed with a paragraph. The term 57 | {person,"Joe"} is an Erlang term which has been typeset 58 | in 12 point courier. The paragraph justification algorithm 59 | does proper kerning so, for example, the word AWAY is 60 | correctly kerned! - line breaks within a paragraph are selected by a 61 | sophisticated global optimization technique.

62 | 63 |
64 | 65 |
66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /test/ebutik.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richcarl/erlguten/debd6120907e5e18b4bca5fbd5bdd9cb653f282d/test/ebutik.jpg -------------------------------------------------------------------------------- /test/eg_test1.erl: -------------------------------------------------------------------------------- 1 | %%========================================================================== 2 | %% Copyright (C) 2003 Mikael Karlsson 3 | %% 4 | %% Permission is hereby granted, free of charge, to any person obtaining a 5 | %% copy of this software and associated documentation files (the 6 | %% "Software"), to deal in the Software without restriction, including 7 | %% without limitation the rights to use, copy, modify, merge, publish, 8 | %% distribute, sublicense, and/or sell copies of the Software, and to permit 9 | %% persons to whom the Software is furnished to do so, subject to the 10 | %% following conditions: 11 | %% 12 | %% The above copyright notice and this permission notice shall be included 13 | %% in all copies or substantial portions of the Software. 14 | %% 15 | %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | %% OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | %% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 18 | %% NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 19 | %% DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 | %% OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 21 | %% USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | %% 23 | %% Authors: Mikael Karlsson 24 | %% Purpose: Test PDF documents main api 25 | %%========================================================================== 26 | 27 | -module(eg_test1). 28 | 29 | -export([test/0]). 30 | 31 | test()-> 32 | PDF = eg_pdf:new(), 33 | eg_pdf:set_pagesize(PDF,a4), 34 | eg_pdf:set_author(PDF,"Mikael Karlsson"), 35 | eg_pdf:set_title(PDF, "Test of PDF API"), 36 | eg_pdf:set_subject(PDF,"PDF is cool, but Erlang is cooler"), 37 | eg_pdf:set_keywords(PDF,"Erlang, PDF, Gutenberg"), 38 | %% eg_pdf:set_date(PDF,2003,3,31), 39 | draft(PDF), 40 | eg_pdf:new_page(PDF), 41 | draft(PDF), 42 | 43 | eg_pdf:set_page(PDF,1), 44 | eg_pdf:image(PDF,'joenew.jpg',{390,440},{height,140}), 45 | eg_pdf:image(PDF,'joenew.jpg',{390,200},{width,140}), 46 | eg_pdf:image(PDF,'joenew.jpg',{190,300},{140,140}), 47 | eg_pdf:begin_text(PDF), 48 | eg_pdf:set_font(PDF, "Times-Italic", 240), 49 | eg_pdf:set_text_pos(PDF, 60,600), 50 | eg_pdf:textbr(PDF, "Wow"), 51 | eg_pdf:set_font(PDF, "Times-Roman", 42), 52 | eg_pdf:set_text_pos(PDF, 60,-40), 53 | eg_pdf:set_text_leading(PDF,60), 54 | eg_pdf:text(PDF, "Welcome home "), 55 | eg_pdf:set_text_rise(PDF, 20), 56 | eg_pdf:textbr(PDF, "Joe, "), 57 | eg_pdf:set_text_rise(PDF, 0), 58 | eg_pdf:textbr(PDF, "hope you had a"), 59 | eg_pdf:textbr(PDF, "nice trip"), 60 | eg_pdf:end_text(PDF), 61 | 62 | eg_pdf:set_page(PDF,2), 63 | 64 | eg_pdf:begin_text(PDF), 65 | eg_pdf:set_font(PDF,"Times-Roman", 24), 66 | eg_pdf:set_text_pos(PDF,60,750), 67 | eg_pdf:set_text_leading(PDF,26), 68 | eg_pdf:textbr(PDF, "Times-Roman 24 pt"), 69 | eg_pdf:set_font(PDF, "Times-Italic", 16), 70 | eg_pdf:textbr(PDF,"Times-Italic 16 pt"), 71 | eg_pdf:set_font(PDF, "Courier", 6), 72 | eg_pdf:textbr(PDF, "Courier 6 pt"), 73 | eg_pdf:set_text_leading(PDF,14), 74 | eg_pdf:set_font(PDF,"Blahonga", 12), 75 | eg_pdf:textbr(PDF, "The blahonga font will fall back to Times-Roman"), 76 | eg_pdf:textbr(PDF, "This is a check of ( ) \\ escape chars"), 77 | eg_pdf:kernedtext(PDF, 78 | [ "This is a test of Kerning: A", 120, "W", 79 | 120, "A", 95, "Y again" ]), 80 | eg_pdf:break_text(PDF), 81 | eg_pdf:textbr(PDF, "This is a text without Kerning: AWAY again"), 82 | eg_pdf:break_text(PDF), 83 | eg_pdf:end_text(PDF), 84 | 85 | eg_pdf:new_page(PDF), 86 | eg_pdf:line(PDF,100,100,200,100), 87 | eg_pdf:bezier(PDF,100,100,100,200,200,200,200,100), 88 | eg_pdf:path(PDF,stroke), 89 | eg_pdf:set_fill_color_RGB(PDF,0.5,0.7,0.2), 90 | eg_pdf:bezier(PDF,300,100,300,200,500,200,500,100), 91 | eg_pdf:path(PDF,close_fill_stroke), 92 | eg_pdf:set_fill_color(PDF,purple), 93 | eg_pdf:bezier(PDF,{300,400},{300,450},{500,450},{500,400}), 94 | eg_pdf:bezier_c(PDF,{500,350},{400,350},{300,400}), 95 | eg_pdf:path(PDF,fill), 96 | eg_pdf:set_dash(PDF,dash), 97 | eg_pdf:set_fill_color(PDF,slateblue), 98 | eg_pdf:line(PDF,100,250,400,250), 99 | eg_pdf:poly(PDF,[{100,300},{150,350},{200,350},{250,300}]), 100 | eg_pdf:path(PDF,fill_stroke), 101 | eg_pdf:set_dash(PDF,solid), 102 | eg_pdf:set_stroke_color(PDF,khaki), 103 | eg_pdf:circle(PDF, {200,200}, 200), 104 | eg_pdf:path(PDF, stroke), 105 | eg_pdf:set_stroke_color(PDF, {16#00,16#FF,16#00}), 106 | eg_pdf:circle(PDF, {200,300}, 50), 107 | eg_pdf:path(PDF, stroke), 108 | eg_pdf:ellipse(PDF, {200,300}, {50,100}), 109 | eg_pdf:path(PDF, stroke), 110 | eg_pdf:ellipse(PDF, {200,300}, {100,50}), 111 | eg_pdf:path(PDF, stroke), 112 | eg_pdf:circle(PDF, {200,300}, 100), 113 | eg_pdf:path(PDF, stroke), 114 | eg_pdf:grid(PDF,[50,100,150],[600,700,800]), 115 | eg_pdf:round_rect(PDF,{300,600},{200,100},20), 116 | eg_pdf:path(PDF, stroke), 117 | eg_pdf:rectangle(PDF,{300,600},{200,100}, stroke), 118 | eg_pdf:new_page(PDF), 119 | colortest(PDF), 120 | {Serialised, _PageNo} = eg_pdf:export(PDF), 121 | file:write_file("eg_test1.pdf",[Serialised]), 122 | eg_pdf:delete(PDF). 123 | 124 | %% Write a DRAFT text rotated in the background 125 | draft(PDF)-> 126 | eg_pdf:save_state(PDF), 127 | pageno(PDF), 128 | eg_pdf:set_fill_gray(PDF,0.75), 129 | eg_pdf:rectangle(PDF, 100,800,410,5, fill), 130 | eg_pdf:rectangle(PDF, 100,42,410,5,fill_then_stroke), 131 | eg_pdf:translate(PDF,150,650), 132 | eg_pdf:mirror_yaxis(PDF,300), 133 | eg_pdf:rotate(PDF,300), 134 | eg_pdf:begin_text(PDF), 135 | eg_pdf:set_font(PDF,"Helvetica-Bold", 160), 136 | eg_pdf:set_text_rendering(PDF, fill), 137 | eg_pdf:set_text_pos(PDF, 0,0), 138 | eg_pdf:textbr(PDF, "DRAFT"), 139 | eg_pdf:end_text(PDF), 140 | eg_pdf:restore_state(PDF). 141 | 142 | pageno(PDF)-> 143 | eg_pdf:begin_text(PDF), 144 | eg_pdf:set_font(PDF,"Times-Roman", 11), 145 | A = eg_pdf:get_page_no(PDF), 146 | Str = "Page " ++ eg_pdf_op:n2s(A), 147 | Width = eg_pdf:get_string_width(PDF,"Times-Roman", 11, Str), 148 | case A rem 2 of 149 | 0 -> 150 | eg_pdf:set_text_pos(PDF, 100, 50); 151 | 1 -> 152 | eg_pdf:set_text_pos(PDF, 510 - Width, 50) 153 | end, 154 | eg_pdf:text(PDF, "Page " ++ eg_pdf_op:n2s(A)), 155 | eg_pdf:end_text(PDF). 156 | 157 | 158 | colortest(PDF)-> 159 | D=50,S=750, M=60, 160 | eg_pdf:save_state(PDF), 161 | eg_pdf:translate(PDF, M, S), 162 | colortest1(PDF,0,[white,silver,gray,black,maroon,red,fuchsia,purple,lime, 163 | green,olive,yellow,navy,blue,teal,aqua]), 164 | eg_pdf:restore_state(PDF), 165 | eg_pdf:save_state(PDF), 166 | eg_pdf:translate(PDF, M, S-D), 167 | colortest1(PDF,0,[blue2,blue3,blue4,blueviolet,cornflowerblue,darkorchid, 168 | darkslateblue,dodgerblue,lightskyblue,mediumblue, 169 | mediumpurple, mediumslateblue,midnightblue,purpleblue, 170 | royalblue,skyblue2, slateblue]), 171 | eg_pdf:restore_state(PDF), 172 | eg_pdf:save_state(PDF), 173 | eg_pdf:translate(PDF, M, S-2*D), 174 | colortest1(PDF,0,[aquamarine4,cadetblue,darkturquoise,lightblue, 175 | lightseagreen,lightslategray,lightsteelblue, 176 | mediumturquoise,paleturquoise,powderblue,skyblue, 177 | steelblue,turquoise]), 178 | eg_pdf:restore_state(PDF), 179 | eg_pdf:save_state(PDF), 180 | eg_pdf:translate(PDF, M, S-3*D), 181 | colortest1(PDF,0,[antiquewhite3,antiquewhite4,azure3, beige,darkslategray, 182 | gainsboro,honeydew,slategray,thistle 183 | ]), 184 | eg_pdf:restore_state(PDF), 185 | eg_pdf:save_state(PDF), 186 | eg_pdf:translate(PDF, M, S-4*D), 187 | colortest1(PDF,0,[aquamarine,chartreuse,darkgreen,darkseagreen,forestgreen, 188 | green2,green3,green4,greenyellow,lawngreen,limegreen, 189 | mediumaquamarine,mediumseagreen,mediumspringgreen, 190 | olivedrab,palegreen]), 191 | eg_pdf:restore_state(PDF), 192 | eg_pdf:save_state(PDF), 193 | eg_pdf:translate(PDF, M, S-5*D), 194 | colortest1(PDF,0,[seagreen,springgreen,yellowgreen]), 195 | eg_pdf:restore_state(PDF), 196 | eg_pdf:save_state(PDF), 197 | eg_pdf:translate(PDF, M, S-6*D), 198 | colortest1(PDF,0,[magenta,magenta2,magenta3,magenta4,mediumorchid,orchid, 199 | plum,violet]), 200 | eg_pdf:restore_state(PDF), 201 | eg_pdf:save_state(PDF), 202 | eg_pdf:translate(PDF, M, S-7*D), 203 | colortest1(PDF,0,[brown,burlywood,chocolate,coral,darkgoldenrod,darkorange, 204 | darksalmon,deeppink,firebrick,gold,goldenrod,hotpink, 205 | indianred,lightcoral,lightpink,lightsalmon]), 206 | eg_pdf:restore_state(PDF), 207 | eg_pdf:save_state(PDF), 208 | eg_pdf:translate(PDF, M, S-8*D), 209 | colortest1(PDF,0,[maroon0,orange,orangered,palevioletred,peachpuff,peru, 210 | pink,red2,red3,red4,rosybrown,salmon,sandybrown,sienna, 211 | tomato,violetred]), 212 | eg_pdf:restore_state(PDF), 213 | eg_pdf:save_state(PDF), 214 | eg_pdf:translate(PDF, M, S-9*D), 215 | colortest1(PDF,0,[aliceblue,azure,floralwhite,ghostwhite,ivory,lavender, 216 | lavenderblush,lightcyan,lightyellow,linen,mintcream, 217 | mistyrose,oldlace,seashell,snow,whitesmoke]), 218 | eg_pdf:restore_state(PDF), 219 | eg_pdf:save_state(PDF), 220 | eg_pdf:translate(PDF, M, S-10*D), 221 | colortest1(PDF,0,[antiquewhite,bisque,blancedalmond,comsilk,darkkhaki, 222 | darkolivegreen,khaki,lemonchiffon,lightgoldenrod, 223 | lightgoldenrodyellow]), 224 | eg_pdf:restore_state(PDF), 225 | eg_pdf:save_state(PDF), 226 | eg_pdf:translate(PDF, M, S-11*D), 227 | colortest1(PDF,0,[moccasin,palegoldenrod,papayawhip, 228 | tan,wheat,yellow2,yellow3,yellow4]), 229 | 230 | eg_pdf:restore_state(PDF). 231 | 232 | colortest1(_PDF,_N,[])-> 233 | []; 234 | colortest1(PDF,N,[H|T])-> 235 | eg_pdf:set_fill_color(PDF,H), 236 | eg_pdf:rectangle(PDF,{0,20},{20,20}), 237 | eg_pdf:path(PDF,fill_stroke), 238 | eg_pdf:set_fill_color(PDF,black), 239 | eg_pdf:begin_text(PDF), 240 | eg_pdf:set_font(PDF,"Times-Roman", 8), 241 | eg_pdf:set_text_pos(PDF,0,(N rem 2)*10), 242 | eg_pdf:text(PDF,atom_to_list(H)), 243 | eg_pdf:end_text(PDF), 244 | eg_pdf:translate(PDF,30,0), 245 | colortest1(PDF,N+1,T). 246 | -------------------------------------------------------------------------------- /test/eg_test2.erl: -------------------------------------------------------------------------------- 1 | %%========================================================================== 2 | %% Copyright (C) 2003 Joe Armstrong 3 | %% 4 | %% Permission is hereby granted, free of charge, to any person obtaining a 5 | %% copy of this software and associated documentation files (the 6 | %% "Software"), to deal in the Software without restriction, including 7 | %% without limitation the rights to use, copy, modify, merge, publish, 8 | %% distribute, sublicense, and/or sell copies of the Software, and to permit 9 | %% persons to whom the Software is furnished to do so, subject to the 10 | %% following conditions: 11 | %% 12 | %% The above copyright notice and this permission notice shall be included 13 | %% in all copies or substantial portions of the Software. 14 | %% 15 | %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | %% OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | %% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 18 | %% NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 19 | %% DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 | %% OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 21 | %% USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | %% 23 | %% Author: Joe Armstrong 24 | %% Last Edit: 2003-03-11 25 | %% Purpose: Test cases 26 | %%========================================================================== 27 | 28 | 29 | %% This is for the brave who want to try writing PDF by hand 30 | %% The ouput of this program is a total mess :-) 31 | %% To run this program evaluate 32 | %% eg_test2:test() - this makes a two page PDF document 33 | %% called default.pdf 34 | 35 | 36 | -module(eg_test2). 37 | 38 | -include("../src/eg.hrl"). 39 | 40 | -export([test/0]). 41 | 42 | 43 | %% ============================================================================ 44 | 45 | test() -> 46 | Info = #info{creator="Erlang", 47 | creationDate={{2003,2,15},{15,20,11}}, 48 | producer="mkPdf", 49 | author="", 50 | title="", 51 | subject="", 52 | keywords="ErlangKeyword"}, 53 | Fonts = ["Times-Roman", "Times-Italic", "Courier"], 54 | %% = FR FI FC 55 | Pages = [page(1),page(2),page(3)], 56 | Pages1 = zap(Pages), 57 | MediaBox={0,0,595,842}, 58 | Fonts1 = lists:map(fun(I) -> eg_font_map:handler(I) end, Fonts), 59 | eg_pdf_assemble:make_pdf_file("eg_test2.pdf", 60 | Info, Fonts1, Pages1, MediaBox). 61 | 62 | zap(Pages) -> 63 | FR = cmd("Times-Roman"), 64 | FI = cmd("Times-Italic"), 65 | FC = cmd("Courier"), 66 | lists:map(fun({page,I}) -> {page, xform(I, FR, FI, FC)} end, Pages). 67 | 68 | cmd(F) -> 69 | M = eg_font_map:handler(F), 70 | "/F" ++ eg_pdf_op:i2s(M:index()) ++ " ". 71 | 72 | xform("/FR " ++ T, FR, FI, FC) -> FR ++ xform(T, FR, FI, FC); 73 | xform("/FI " ++ T, FR, FI, FC) -> FI ++ xform(T, FR, FI, FC); 74 | xform("/FC " ++ T, FR, FI, FC) -> FC ++ xform(T, FR, FI, FC); 75 | xform([H|T], FR, FI, FC) -> [H|xform(T, FR, FI, FC)]; 76 | xform([], _, _, _) -> []. 77 | 78 | %% Two pages of PDF 79 | page(1) -> 80 | {page, 81 | " 82 | 0.6000 g 0.6000 G 83 | 20 750 m 100 750 l S 84 | BT 85 | /FI 240 Tf 86 | -1.0000 -0.0000 0.0000 -1.0000 600 600 Tm (Wow)Tj 87 | 88 | /FR 14 Tf 89 | 1 0 0 1 20 750 Tm 90 | 18 0 Td (Line 1 indent 36 point) Tj 91 | -18 -16 Td (line 2 indebt 0) Tj 92 | 0 -16 Td (line 3 indent 0) Tj 93 | 0 -16 Td (line 4 indent 0) Tj 94 | 12 -16 Td (line 5 indent 12 points) Tj 95 | -12 -16 Td (line 6 indent 0 ) Tj 96 | 97 | ET 98 | " 99 | }; 100 | %% page 2 has a lot of junk so I can check my 101 | %% computation of units is correct 102 | page(2) -> 103 | {page, 104 | " 105 | BT 106 | 107 | %% width of a=444 b=500 space=250 108 | %% (aaa bbb) = 3*444+3*500+250 = 3082*16/1000 = 49.31 (50+49.31) = 99.31 109 | 110 | /FR 16 Tf 18 TL 1 0 0 1 50 450 Tm [(aaa bbb) 0 ] TJ 111 | 112 | ET 113 | 114 | 50 475 m 115 | 50 400 l 116 | 99.31 400 l 117 | 99.31 475 l S 118 | 119 | %% same as above (width = 3082-300) = 2782*16/1000 = 44.51 + 200 = 244.51 120 | 121 | BT 122 | /FR 16 Tf 18 TL 1 0 0 1 200 450 Tm [(aa)300(a) ( bbb) 0 ] TJ 123 | ET 124 | 125 | 200 475 m 126 | 200 400 l 127 | 244.51 400 l 128 | 244.51 475 l S 129 | 130 | %% normal width = 3082 (extra) = 131 | %% total = 3082 + 880 = 3962 => *16/1000 = 63.39 + 300 = 363.39 132 | 133 | BT 134 | /FR 16 Tf 18 TL 1 0 0 1 300 450 Tm 52 Tw [(aaa bbb) 0 ] TJ 135 | ET 136 | 137 | %% Calculate (aaa bbb) = 3*444+3*500+2*250 138 | %% = 3332 *16/1000 = 53.31 + 2*52 = 157.31 139 | 140 | 300 475 m 141 | 300 400 l 142 | 457.31 400 l 143 | 457.31 475 l S 144 | 145 | BT 146 | 147 | /FR 24 Tf 20 750 Td (Times-Roman 24 pt) Tj 148 | 26 TL T* 149 | /FI 16 Tf (Times-Italic 16)Tj 150 | T* 151 | /FC 6 Tf (Courier 6)Tj 152 | T* 153 | /FR 12 Tf (The tuple) Tj 154 | /FI 12 Tf ( person, ) Tj 155 | /FR 12 Tf (for example, is written,) Tj 156 | /FC 12 Tf ({person,\"joe\"}) Tj 157 | /FR 12 Tf ( and represents a person.) Tj 158 | 159 | 160 | /FR 16 Tf 161 | 18 TL 162 | 1 0 0 1 50 650 Tm 163 | (Hello) Tj 164 | T* 165 | (Joe) Tj 166 | 0.60000 g 167 | 0.60000 G 168 | 169 | 1 0 0 1 100 600 Tm 170 | (100 x 600 0.6 gray text) Tj 171 | 172 | 1 0 0 rg 173 | 1 0 0 RG 174 | 175 | 1 0 0 1 150 550 Tm 176 | (150 x 550 red text) Tj 177 | 178 | 0.9659 0.2588 -0.2588 0.9659 100 500 Tm 179 | (100x400 red text with 15 degree rotation) Tj 180 | 181 | -1 0 0 -1 200 500 Tm 182 | (Upside down text) Tj 183 | 184 | 0 0 1 rg 185 | 0 0 1 RG 186 | 0 1 -1 0 30 500 Tm 187 | (90 degree blue text) Tj 188 | 189 | 0 1 0 rg 190 | 0 1 0 RG 191 | 192 | 1 0 0 1 350 650 Tm 193 | (Testing Hello World - abcWAdef ) Tj 194 | T* 195 | [ (T) 70 (esting) (T) 300 (esting) -3000 (abcW) 120 (Adef.) ] TJ 196 | T* 197 | 1 Tr 198 | (PDF )Tj 199 | -5 Ts (is ) Tj 200 | -10 Ts (really ) Tj 201 | -15 Ts (fun ) Tj 202 | -20 Ts (stuff ) Tj 203 | ET 204 | 205 | 450 720 m 206 | 150 720 l 207 | S 208 | 209 | 1.0 0.0 0.0 RG 210 | 0 1 0 rg 211 | 212 | 500 700 50 75 re B 213 | 214 | " 215 | }; 216 | page(3) -> 217 | {page, 218 | " 219 | % experiment with text rotation and special effects 220 | % NOTE xpdf does not display this correctly acrobat does :-) 221 | BT 222 | 223 | /FR 80 Tf 224 | 225 | % translate to 50 650 226 | 1 0 0 1 50 650 Tm (ABC)Tj 227 | 228 | /FC 60 Tf 229 | % translate 300 650 scale x by 0.2 230 | 1 0 0 1 20 800 Tm (courier unscaled) Tj 231 | 0.8 0 0 1 20 750 Tm (courier scaled)Tj 232 | 233 | /FR 80 Tf 234 | 235 | % 10 degree rotation cos(10) = 0.9848 sin(10)=0.1736 236 | % matric = cos sin -sin cos X Y 237 | 238 | 0.9848 0.1736 -0.1736 0.9848 50 550 Tm (ROT 10) Tj 239 | 240 | %% 10 degress skew 241 | %% tan(10) = 0.176327 242 | % matrix = 1 tan(A) tan(B) 1 0 0] 243 | 244 | 1 0.1763 0.1763 1 50 480 Tm (Skew 10) Tj 245 | 246 | -1 0 0 1 300 400 Tm (Mirror) Tj 247 | 248 | -1 0 0 1 300 320 Tm (rorriM) Tj 249 | 250 | 1 0 0 1 300 500 Tm 1 0 0 rg (Red) Tj 0 0 0 rg (Black) Tj 251 | 252 | ET 253 | " 254 | }. 255 | 256 | 257 | 258 | 259 | 260 | 261 | -------------------------------------------------------------------------------- /test/eg_test3.erl: -------------------------------------------------------------------------------- 1 | %%========================================================================== 2 | %% Copyright (C) 2003 Joe Armstrong 3 | %% 4 | %% Permission is hereby granted, free of charge, to any person obtaining a 5 | %% copy of this software and associated documentation files (the 6 | %% "Software"), to deal in the Software without restriction, including 7 | %% without limitation the rights to use, copy, modify, merge, publish, 8 | %% distribute, sublicense, and/or sell copies of the Software, and to permit 9 | %% persons to whom the Software is furnished to do so, subject to the 10 | %% following conditions: 11 | %% 12 | %% The above copyright notice and this permission notice shall be included 13 | %% in all copies or substantial portions of the Software. 14 | %% 15 | %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | %% OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | %% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 18 | %% NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 19 | %% DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 | %% OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 21 | %% USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | %% 23 | %% Author: Joe Armstrong 24 | %% Purpose: Test cases 25 | %%========================================================================== 26 | 27 | -module(eg_test3). 28 | 29 | -export([test/0, norm/0]). 30 | 31 | -include("../src/eg.hrl"). 32 | 33 | %% ============================================================================ 34 | 35 | box(PDF, Color, X, Y, W, H) -> 36 | eg_pdf:set_fill_color(PDF, Color), 37 | eg_pdf:rectangle(PDF,{X, Y},{W,H}), 38 | eg_pdf:path(PDF,fill), 39 | eg_pdf:set_fill_color(PDF,black). 40 | 41 | 42 | test() -> 43 | PDF = eg_pdf:new(), 44 | eg_pdf:set_pagesize(PDF,a4), 45 | eg_pdf:set_page(PDF,1), 46 | eg_pdf:show_grid(PDF, a4), 47 | box(PDF, moccasin, 50, 300, 225, 350), 48 | zap(PDF, gold, title, 20, 830, 66, 24, 24, 1), 49 | zap(PDF, simple, 60, 650, 35, 14, 16, 5), 50 | zap(PDF, simple, 60, 560, 30, 14, 16, 5), 51 | zap(PDF, whitesmoke, two, 300, 760, 44, 18,20, 3), 52 | zap(PDF, romanAndCourier1, 60, 360, 35, 14, 16, 7), 53 | zap(PDF, palegreen, complex, 400, 600, 26, 12, 14, 22), 54 | zap(PDF, 5, 60, 450, 35, 12, 14, 6), 55 | zap(PDF, azure, narrow, 280, 650, 16, 8,10, 38), 56 | %% eg_pdf:show_grid(PDF, a4), 57 | eg_pdf:image(PDF,'./joenew.jpg',{50, 650},{width,200}), 58 | {Serialised, _PageNo} = eg_pdf:export(PDF), 59 | file:write_file("eg_test3.pdf",[Serialised]), 60 | eg_pdf:delete(PDF). 61 | 62 | ensure_fonts_are_loaded(PDF, {_,TagMap}) -> 63 | lists:foreach(fun({_,Face}) -> 64 | FontHandler = eg_richText:fontFromFace(Face), 65 | Font = FontHandler:fontName(), 66 | eg_pdf:ensure_font_gets_loaded(PDF, Font) 67 | end, TagMap). 68 | 69 | 70 | zap(PDF, Color, Sample, X, Y, Measure, PtSize, Leading, NLines) -> 71 | Width = Measure*6 + 20, 72 | Ht = NLines * PtSize + 20, 73 | box(PDF, Color, X, Y-Ht+10, Width, Ht), 74 | zap(PDF, Sample, X+10, Y+10, Measure, PtSize, Leading, NLines). 75 | 76 | zap(PDF, Sample, X, Y, Measure, PtSize, Leading, NLines) -> 77 | %% Measure in picas 78 | Len = Measure*6, 79 | Xml = parse_xml_para_str(xml(Sample)), 80 | %% io:format("XML=~p~n",[Xml]), 81 | TagMap = eg_xml2richText:default_tagMap(PtSize), 82 | ensure_fonts_are_loaded(PDF, TagMap), 83 | Norm = eg_xml2richText:normalise_xml(Xml, TagMap), 84 | %% io:format("Norm=~p~n",[Norm]), 85 | {p, _, RichText} = Norm, 86 | Widths = [Len-20|lists:duplicate(NLines-1, Len)], 87 | Off = [20|lists:duplicate(NLines-1, 0)], 88 | case eg_line_break:break_richText(RichText, {justified, Widths}) of 89 | impossible -> 90 | io:format("Cannot break line are widths ok~n"); 91 | {Lines,_,_} -> 92 | Code = eg_richText2pdf:richText2pdf(X, Y, justified, 0, Lines, 93 | Leading, Widths, Off), 94 | eg_pdf:begin_text(PDF), 95 | eg_pdf:append_stream(PDF, Code), 96 | eg_pdf:end_text(PDF) 97 | %% eg_pdf_lib:draw_box(PDF, X, Y, Len, Leading, NLines) 98 | end. 99 | 100 | parse_xml_para_str(Str) -> 101 | [{xml, XmlPara}] = eg_xml_lite:parse_all_forms(Str), 102 | XmlPara. 103 | 104 | 105 | %%---------------------------------------------------------------------- 106 | %% test data sets 107 | 108 | %% Here are some widths 109 | %% Times Roman A = 722 110 | %% space = 250 111 | %% a = 444 112 | %% b = 500 113 | %% c = 444 114 | %% W = 944 115 | 116 | xml(1) -> 117 | "

aaa aaa aaa aaa bbb ccc ddd eee aaa ddd ss aaa aaa aaa 118 | bbb bbb bbb bbb bbb bbb bbb ccc ddd 119 |

"; 120 | xml(narrow) -> 121 | "

This is a long narrow box set in Times-Roman. Times-Roman was 122 | designed for printing long and narrow newspaper columns. It actually looks 123 | pretty horrid if set in wide measures. This is set narrow and tight. The 124 | really catestrophic thing about Times-Roman is that is is probably the 125 | most commonly used typeface, despite the fact it in manifestly 126 | unsuitable for the purpose it is being used for. Using narrow columns 127 | you can cram in loads of virtually unreadable data - no body will thank 128 | you, apart from environmentalists, who, I suppose will be pleased at the 129 | number of trees which are being saved.

"; 130 | xml(simple) -> 131 | "

This is normal text, with no emphasised code, 132 | the next example will be more complicated. This example 133 | is just simple text. In the next example I will show some 134 | text with emphasis.

"; 135 | xml(two) -> 136 | "

This is normal text, with a small 137 | ammount of emphasised text. 138 | This example only has two typefaces.

"; 139 | xml(romanAndCourier1) -> 140 | "

This is normal text, with a small 141 | ammount of courier text. 142 | This example only has two typefaces.

"; 143 | xml(complex) -> 144 | "

This is normal text, set 5 picas wide in 12/14 Times Roman. 145 | I even allow some emphasised term, set in Times-Italic. The TeX 146 | hyphenation algorithm is also implemented. 147 | I have also some cursive text and an example of 148 | an Erlang term. The term {person, \"Joe\"} is an Erlang term. 149 | The variable X, was immediately followed by 150 | a comma. The justification algorithm does proper kerning, 151 | which is more than Microsoft Word can do. AWAY again is 152 | correctly kerned! Erlang terms {like, this} 153 | are typeset in courier.

"; 154 | xml(4) -> 155 | "

This is Times Roman.

"; 156 | xml(5) -> "

is AWAY correctly kerned? Erlang terms 157 | {like, this}are typeset in courier. 158 | The red terms are typeset in ZapfChancery-MediumItalic. 159 | Then I can set blue terms as well.

"; 160 | xml(title) -> 161 | "

This page tests justification routines

". 162 | 163 | norm() -> "(This is normal text, with some **emphasised code**, I have 164 | also some *cursive text* and an example of and Erlang term. The 165 | term <{person, \"Joe\"}> is an Erlang term. The variable , was 166 | immediately followed by a comma.)". 167 | 168 | 169 | -------------------------------------------------------------------------------- /test/eg_test4.erl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richcarl/erlguten/debd6120907e5e18b4bca5fbd5bdd9cb653f282d/test/eg_test4.erl -------------------------------------------------------------------------------- /test/eg_test5.erl: -------------------------------------------------------------------------------- 1 | %%========================================================================== 2 | %% Copyright (C) 2003 Joe Armstrong 3 | %% 4 | %% Permission is hereby granted, free of charge, to any person obtaining a 5 | %% copy of this software and associated documentation files (the 6 | %% "Software"), to deal in the Software without restriction, including 7 | %% without limitation the rights to use, copy, modify, merge, publish, 8 | %% distribute, sublicense, and/or sell copies of the Software, and to permit 9 | %% persons to whom the Software is furnished to do so, subject to the 10 | %% following conditions: 11 | %% 12 | %% The above copyright notice and this permission notice shall be included 13 | %% in all copies or substantial portions of the Software. 14 | %% 15 | %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | %% OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | %% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 18 | %% NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 19 | %% DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 | %% OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 21 | %% USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | %%========================================================================== 23 | 24 | -module(eg_test5). 25 | 26 | -export([test/0, test/1]). 27 | 28 | 29 | test() -> 30 | test(9). 31 | 32 | test(1) -> 33 | text(1,11); 34 | test(2) -> 35 | %% Noraml para test - fits exactly into three lines 36 | eg_line_break:break_richText(text(1,11), {justified, [190,190,190]}); 37 | test(3) -> 38 | %% This has some splill 39 | eg_line_break:break_richText(text(1,11), {justified, [190,190]}); 40 | test(4) -> 41 | eg_line_break:break_richText(text(1,11), {ragged, [190,190,190,190]}); 42 | test(5) -> 43 | %% fits exactly 44 | eg_line_break:break_richText(text(1,11), 45 | {preformatted, [200,200,200,200,200,200,200]}); 46 | test(6) -> 47 | %% Has some spill 48 | eg_line_break:break_richText(text(1,11), {preformatted, [200,200,200]}); 49 | test(7) -> 50 | eg_line_break:break_richText(text(1,11), 51 | {centered, [210,210,210,210,210,210,210,210]}); 52 | %% test the line breaker 53 | test(8) -> 54 | W1 = eg_richText:mk_test_word("Have"), 55 | W2 = eg_richText:mk_test_word("a"), 56 | W3 = eg_richText:mk_test_word("splended"), 57 | W4 = eg_richText:mk_test_word("opportunity"), 58 | eg_line_break:make_partitions([W1,W2,W3,W4], eg_hyphen_rules_en_GB); 59 | test(9) -> 60 | PDF = eg_pdf:new(), 61 | eg_pdf:set_pagesize(PDF,a4), 62 | eg_pdf:set_page(PDF,1), 63 | eg_pdf:begin_text(PDF), 64 | eg_pdf:set_font(PDF, "Times-Roman", 24), 65 | demo(PDF, 0, 350, 475, 12, 13, 0, 180), 66 | demo(PDF, 45, 325, 600, 11, 12, 0, 190), 67 | demo(PDF, 90, 250, 575, 11, 12, 0, 190), 68 | demo(PDF, 135, 150, 600, 10, 11, 0, 190), 69 | demo(PDF, 180, 300, 200, 14, 16, 0, 130), 70 | demo(PDF, 225, 150, 375, 11, 12, 0, 130), 71 | demo(PDF, 270, 125, 500, 9, 10, 0, 60), 72 | demo(PDF, 315, 375, 375, 12, 13, 10, 120), 73 | eg_pdf:end_text(PDF), 74 | eg_pdf:show_grid(PDF, a4), 75 | {Serialised, _PageNo} = eg_pdf:export(PDF), 76 | file:write_file("eg_test5.pdf",[Serialised]), 77 | eg_pdf:delete(PDF). 78 | 79 | demo(PDF, Rot, X, Y, PointSize, Leading, Offset, Width) -> 80 | Widths = [Width-Offset|lists:duplicate(30,Width)], 81 | Off = [Offset|lists:duplicate(30,0)], 82 | {Lines,_,_} = eg_line_break:break_richText(text(1,Rot,PointSize), 83 | {justified, Widths}), 84 | Code = eg_richText2pdf:richText2pdf(X, Y, justified, Rot, Lines, 85 | Leading, Widths, Off), 86 | io:format("Code=~p~n",[Code]), 87 | eg_pdf:append_stream(PDF, Code). 88 | 89 | % for(0, _F) -> 90 | % true; 91 | % for(N, F) -> 92 | % F(N), 93 | % for(N-1, F). 94 | 95 | % justified_para(Leading, Offset, Width, N) -> 96 | % justified_para(Leading, Offset, Width, 0, N). 97 | 98 | % justified_para(Leading, Offset, Width, Rot, N) -> 99 | % {justified, Leading, Rot, 100 | % [Offset|lists:duplicate(N-1, 0)], 101 | % [Width-Offset|lists:duplicate(N-1,Width)]}. 102 | 103 | % ragged_para(Leading, Offset, Width, N) -> 104 | % {ragged, Leading, 105 | % [Offset|lists:duplicate(N-1, 0)], 106 | % [Width-Offset|lists:duplicate(N-1,Width)]}. 107 | 108 | text(N, Pts) -> 109 | text(N, 0, Pts). 110 | 111 | text(1, Rot, Pts) -> 112 | eg_richText:str2richText("Rotation =" ++ eg_pdf_op:i2s(Rot) ++ 113 | " Hello joe how are you today? 114 | May I take this opportunity 115 | of saying 116 | that my favorite color is blue. 117 | Have a nice day, 118 | from Mr. C. Computer.", Pts). 119 | 120 | -------------------------------------------------------------------------------- /test/eg_test6.erl: -------------------------------------------------------------------------------- 1 | %%========================================================================== 2 | %% Copyright (C) 2003 Joe Armstrong 3 | %% 4 | %% Permission is hereby granted, free of charge, to any person obtaining a 5 | %% copy of this software and associated documentation files (the 6 | %% "Software"), to deal in the Software without restriction, including 7 | %% without limitation the rights to use, copy, modify, merge, publish, 8 | %% distribute, sublicense, and/or sell copies of the Software, and to permit 9 | %% persons to whom the Software is furnished to do so, subject to the 10 | %% following conditions: 11 | %% 12 | %% The above copyright notice and this permission notice shall be included 13 | %% in all copies or substantial portions of the Software. 14 | %% 15 | %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | %% OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | %% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 18 | %% NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 19 | %% DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 | %% OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 21 | %% USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | %% 23 | %% Authors: Joe Armstrong 24 | %% Purpose: Grid planning sheet 25 | %%========================================================================== 26 | 27 | -module(eg_test6). 28 | 29 | -export([test/0]). 30 | 31 | %% ============================================================================ 32 | 33 | test()-> 34 | PDF = eg_pdf:new(), 35 | eg_pdf:set_pagesize(PDF,a4), 36 | eg_pdf:set_page(PDF,1), 37 | %% eg_pdf:set_font(PDF, "GoodCityModern", 40), 38 | eg_pdf:set_font(PDF, "Victorias-Secret", 40), 39 | eg_pdf:moveAndShow(PDF, 50, 700, "Hello Joe from Gutenburg"), 40 | 41 | {Serialised, _PageNo} = eg_pdf:export(PDF), 42 | file:write_file("eg_test6.pdf",[Serialised]), 43 | eg_pdf:delete(PDF). 44 | 45 | -------------------------------------------------------------------------------- /test/erlguten: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | erl -pa ../ebin -pa `pwd` -s erlguten batch $1 -s erlang halt 3 | -------------------------------------------------------------------------------- /test/galley_001.gal: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 27 | 28 | 29 | 30 | 31 | 32 | 35 | 36 | 37 | 38 | 39 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /test/galley_002.gal: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /test/joenew.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richcarl/erlguten/debd6120907e5e18b4bca5fbd5bdd9cb653f282d/test/joenew.jpg -------------------------------------------------------------------------------- /test/kd_test1.erl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richcarl/erlguten/debd6120907e5e18b4bca5fbd5bdd9cb653f282d/test/kd_test1.erl -------------------------------------------------------------------------------- /test/template1.tem: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /test/template_001.tem: -------------------------------------------------------------------------------- 1 | 2 | 3 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /test/test1.map: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 | 13 |
14 |

ErlGuten

" 15 |
16 | 17 | 18 |

Using Erlang in typography applications

" 19 |
20 | 21 | 22 |

INTRODUCTION

" 23 |
24 | 25 | 26 |

This is a small footnote set in 8/10 Times Italic

" 27 |
28 | 29 |
30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /test/test1.pdf: -------------------------------------------------------------------------------- 1 | %PDF-1.4 2 | 1 0 obj 3 | << 4 | /Type /Font 5 | /Subtype /Type1 6 | /Name /F3 7 | /BaseFont /Helvetica 8 | /Encoding /MacRomanEncoding 9 | >> 10 | endobj 11 | 2 0 obj 12 | << 13 | /Type /Font 14 | /Subtype /Type1 15 | /Name /F5 16 | /BaseFont /Courier 17 | /Encoding /MacRomanEncoding 18 | >> 19 | endobj 20 | 3 0 obj 21 | << 22 | /Type /Font 23 | /Subtype /Type1 24 | /Name /F1 25 | /BaseFont /Times-Bold 26 | /Encoding /MacRomanEncoding 27 | >> 28 | endobj 29 | 4 0 obj 30 | << 31 | /Type /Font 32 | /Subtype /Type1 33 | /Name /F2 34 | /BaseFont /Times-Italic 35 | /Encoding /MacRomanEncoding 36 | >> 37 | endobj 38 | 5 0 obj 39 | << 40 | /Type /Font 41 | /Subtype /Type1 42 | /Name /F4 43 | /BaseFont /Times-Roman 44 | /Encoding /MacRomanEncoding 45 | >> 46 | endobj 47 | 6 0 obj 48 | << 49 | /F3 1 0 R 50 | /F5 2 0 R 51 | /F1 3 0 R 52 | /F2 4 0 R 53 | /F4 5 0 R 54 | >> 55 | endobj 56 | 7 0 obj 57 | << 58 | /Type /Pages 59 | /Count 1 60 | /MediaBox [ 0 0 595 842 ] 61 | /Kids [ 9 0 R ] 62 | /Resources << 63 | /Font 6 0 R 64 | /ProcSet [ /PDF /Text ] 65 | >> 66 | 67 | >> 68 | endobj 69 | 8 0 obj 70 | <> 71 | stream 72 | BT 73 | 1 0 0 1 100 728 Tm 0 Tw /F1 32 Tf 74 | 0 Tw [ (ErlGuten)0 ] TJ 75 | ET 76 | BT 77 | 1 0 0 1 100 698 Tm 0 Tw /F2 10 Tf 78 | 0 Tw [ (Using Erlang in typo)10 (gr)15 (aphy applications)0 ] TJ 79 | ET 80 | 81 | q 82 | 0.90 0.80 0.60 rg 83 | 101 779 370 22 re f 84 | 85 | Q 86 | BT 87 | 1 0 0 1 106 784 Tm 0 Tw /F3 10 Tf 88 | 0 Tw [ (INTR)20 (ODUCTION)0 ] TJ 89 | ET 90 | BT 91 | 1 0 0 1 100 50 Tm 0 Tw /F2 8 Tf 92 | 0 Tw [ (This is a small footnote set in 8/10 T)55 (imes Italic)0 ] TJ 93 | ET 94 | q 0.4 g 0.4 G 0 w 100 60 m 100 50 l S 460 60 m 460 50 l S 100 60 m 460 60 l S 100 50 m 460 50 l S Q BT 95 | 1 0 0 1 100 666 Tm 12 0 Td 0.34 Tw /F2 12 Tf 96 | 0.34 Tw [ (ErlGuten.)0 ] TJ 97 | /F4 12 Tf 98 | 0.34 Tw [ ( is a system for hiqh quality typesetting, ErlGuten is free soft-)0 ] TJ 99 | -12 -14 Td -1.02 Tw [ (w)10 (are. ErlGuten aims to produce typographic quality PDF directly from XML)0 ] TJ 100 | 0 -14 Td 0 Tw [ (or from a program.)0 ] TJ 101 | ET 102 | BT 103 | 1 0 0 1 100 624 Tm 12 0 Td 0.38 Tw /F4 12 Tf 104 | 0.38 Tw [ ( The aim of ErlGuten is to produce high quality PDF from a layout lan-)0 ] TJ 105 | -12 -14 Td -0.94 Tw [ (guage or from a program. The ErlGuten distrib)20 (ution includes a programming)0 ] TJ 106 | 0 -14 Td 0.48 Tw [ (API, so that Erlang programs can produce PDF - and a typesetting system)0 ] TJ 107 | 0 -14 Td 0 Tw [ (for typesetting documents written in XML.)0 ] TJ 108 | ET 109 | BT 110 | 1 0 0 1 100 568 Tm 12 0 Td -0.04 Tw /F4 12 Tf 111 | -0.04 Tw [ (The document that you are reading w)10 (as authored in XML and typeset by)0 ] TJ 112 | -12 -14 Td 0.49 Tw [ (ErlGuten. The name ErlGuten is chosen because the program is written in)0 ] TJ 113 | 0 -14 Td 0 Tw [ (Erlang - the )0 ] TJ 114 | /F2 12 Tf 115 | 0 Tw [ (Guten)0 ] TJ 116 | /F4 12 Tf 117 | 0 Tw [ ( part is a reference to )0 ] TJ 118 | /F2 12 Tf 119 | 0 Tw [ (Gutenber)37 (g)0 ] TJ 120 | /F4 12 Tf 121 | 0 Tw [ ( the f)10 (ather of printing.)0 ] TJ 122 | ET 123 | BT 124 | 1 0 0 1 100 526 Tm 12 0 Td 0.01 Tw /F4 12 Tf 125 | 0.01 Tw [ (ErlGuten is a system for high quality typesetting, so we tak)10 (e a great deal)0 ] TJ 126 | -12 -14 Td -0.56 Tw [ (of care when formatting te)15 (xt, a lar)18 (ge number of optimizations are performed)0 ] TJ 127 | 0 -14 Td 0.25 Tw [ (which impro)15 (v)15 (e the quality of the printed te)15 (xt. Man)15 (y of these optimizations)0 ] TJ 128 | 0 -14 Td -0.18 Tw [ (are usually only found in e)15 (xpensi)25 (v)15 (e professional type-setting programs. W)80 (e)0 ] TJ 129 | 0 -14 Td -0.40 Tw [ (belie)25 (v)15 (e that WYSIWYG programs ha)20 (v)15 (e destro)10 (yed the f)20 (ine art of typesetting)0 ] TJ 130 | 0 -14 Td 0 Tw [ (- ErlGuten is a modest attempt to impro)15 (v)15 (e the situation.)0 ] TJ 131 | ET 132 | BT 133 | 1 0 0 1 100 442 Tm 12 0 Td 0.70 Tw /F4 12 Tf 134 | 0.70 Tw [ (W)80 (e ha)20 (v)15 (e chosen XML as the input language for it')55 (s wide appeal. XML)0 ] TJ 135 | -12 -14 Td -0.65 Tw [ (pro)15 (vides only a thin abstraction layer o)15 (v)15 (er the typesetting system - so the ad-)0 ] TJ 136 | 0 -14 Td 0.19 Tw [ (v)15 (enturous can use the programming interf)10 (ace to ErlGuten - to directly pro-)0 ] TJ 137 | 0 -14 Td -0.47 Tw [ (duce typographic quality PDF in real-time. W)80 (e e)15 (xpect this f)10 (acility to be use-)0 ] TJ 138 | 0 -14 Td 0 Tw [ (ful for the dynamic generation of documents from web-serv)15 (ers.)0 ] TJ 139 | ET 140 | BT 141 | 1 0 0 1 100 372 Tm 12 0 Td 0.61 Tw /F4 12 Tf 142 | 0.61 Tw [ (In ErlGuten we tak)10 (e the vie)25 (w that the highest le)25 (v)15 (el of abstraction is the)0 ] TJ 143 | -12 -14 Td 0.26 Tw /F2 12 Tf 144 | 0.26 Tw [ (layout)0 ] TJ 145 | /F4 12 Tf 146 | 0.26 Tw [ ( of a document - we are v)15 (ery concerned that the user can specify the)0 ] TJ 147 | 0 -14 Td -0.47 Tw /F2 12 Tf 148 | -0.47 Tw [ (e)20 (xact)0 ] TJ 149 | /F4 12 Tf 150 | -0.47 Tw [ ( position of te)15 (xt on the printed page. At the ne)15 (xt le)25 (v)15 (el of abstraction we)0 ] TJ 151 | 0 -14 Td -0.27 Tw [ (are concerned with the typef)10 (aces that are used to format dif)25 (ferent re)15 (gions of)0 ] TJ 152 | 0 -14 Td 0 Tw [ (the document.)0 ] TJ 153 | ET 154 | BT 155 | 1 0 0 1 100 302 Tm 12 0 Td -0.15 Tw /F4 12 Tf 156 | -0.15 Tw [ (ErlGuten is designed for the production of lar)18 (ge and comple)15 (x documents)0 ] TJ 157 | -12 -14 Td 0.63 Tw [ (with comple)15 (x layout requirements, lik)10 (e )0 ] TJ 158 | /F2 12 Tf 159 | 0.63 Tw [ (ne)15 (wspaper)10 (s)0 ] TJ 160 | /F4 12 Tf 161 | 0.63 Tw [ ( or )0 ] TJ 162 | /F2 12 Tf 163 | 0.63 Tw [ (books)0 ] TJ 164 | /F4 12 Tf 165 | 0.63 Tw [ (. In ErlGuten)0 ] TJ 166 | 0 -14 Td 0.13 Tw [ (layout, content, and document management are considered separate issues.)0 ] TJ 167 | 0 -14 Td 0.07 Tw [ (Layout is )0 ] TJ 168 | /F2 12 Tf 169 | 0.07 Tw [ (template based)0 ] TJ 170 | /F4 12 Tf 171 | 0.07 Tw [ ( - Content is assumed to be stored as a lar)18 (ge num-)0 ] TJ 172 | 0 -14 Td -0.95 Tw [ (ber of documents in a f)20 (ile system or data base, document management is con-)0 ] TJ 173 | 0 -14 Td 0.41 Tw [ (sidered as a mapping operation which tak)10 (es documents in the content data)0 ] TJ 174 | 0 -14 Td 0 Tw [ (base and maps them onto templates to produce hight quality output.)0 ] TJ 175 | ET 176 | BT 177 | 1 0 0 1 100 204 Tm 12 0 Td 0.50 Tw /F4 12 Tf 178 | 0.50 Tw [ (This is normal te)15 (xt, set 30 picas wide in 12/14 T)35 (imes Roman. Man)15 (y di-)0 ] TJ 179 | -12 -14 Td 0.37 Tw [ (f)25 (ferent typef)10 (aces can be used within a paragraph. )0 ] TJ 180 | /F2 12 Tf 181 | 0.37 Tw [ (Emphasized te)20 (xt)0 ] TJ 182 | /F4 12 Tf 183 | 0.37 Tw [ ( is set in)0 ] TJ 184 | 0 -14 Td -0.01 Tw [ (T)35 (imes-Italic. Hyphenation uses the T)70 (eX h)5 (yphenation algorithm. An)15 (y of the)0 ] TJ 185 | 0 -14 Td 1.40 Tw [ (35 b)20 (uilt-in PDF typef)10 (aces with the same point size can be mix)15 (ed with a)0 ] TJ 186 | 0 -14 Td -0.37 Tw [ (paragraph. The term )0 ] TJ 187 | /F5 12 Tf 188 | 0 Tw [ ({person,"Joe"})0 ] TJ 189 | /F4 12 Tf 190 | -0.37 Tw [ ( is an Erlang term which has been)0 ] TJ 191 | 0 -14 Td -0.67 Tw [ (typeset in 12 point )0 ] TJ 192 | /F2 12 Tf 193 | -0.67 Tw [ (courier)0 ] TJ 194 | /F4 12 Tf 195 | -0.67 Tw [ (. The paragraph justif)20 (ication algorithm does prop-)0 ] TJ 196 | 0 -14 Td -0.71 Tw [ (er )0 ] TJ 197 | /F2 12 Tf 198 | -0.71 Tw [ (k)10 (erning)0 ] TJ 199 | /F4 12 Tf 200 | -0.71 Tw [ ( so, for e)15 (xample, the w)10 (ord A)90 (W)120 (A)105 (Y is correctly k)10 (erned! - line breaks)0 ] TJ 201 | 0 -14 Td -0.19 Tw [ (within a paragraph are selected by a sophisticated global optimization tech-)0 ] TJ 202 | 0 -14 Td 0 Tw [ (nique.)0 ] TJ 203 | ET 204 | 205 | endstream 206 | endobj 207 | 9 0 obj 208 | << 209 | /Type /Page 210 | /Parent 7 0 R 211 | /Contents 8 0 R 212 | >> 213 | endobj 214 | 10 0 obj 215 | << 216 | /Type /Catalog 217 | /Pages 7 0 R 218 | >> 219 | endobj 220 | 11 0 obj 221 | << 222 | /Creator (Erlang) 223 | /CreationDate (D:20030215152011) 224 | /Producer (erlguten-2.0) 225 | /Author () 226 | /Title () 227 | /Subject () 228 | /Keywords (ErlangKeyword) 229 | >> 230 | endobj 231 | xref 232 | 0 12 233 | 0000000000 65535 f 234 | 0000000009 00000 n 235 | 0000000127 00000 n 236 | 0000000243 00000 n 237 | 0000000362 00000 n 238 | 0000000483 00000 n 239 | 0000000603 00000 n 240 | 0000000684 00000 n 241 | 0000000845 00000 n 242 | 0000006837 00000 n 243 | 0000006906 00000 n 244 | 0000006960 00000 n 245 | trailer 246 | << 247 | /Size 12 248 | /Root 10 0 R 249 | /Info 11 0 R 250 | >> 251 | startxref 252 | 7131 253 | %%EOF 254 | -------------------------------------------------------------------------------- /test/test1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

ErlGuten

" 6 |
7 | 8 | 9 |

Using Erlang in typography applications

" 10 |
11 | 12 | 13 |

INTRODUCTION

" 14 |
15 | 16 | 17 |

This is a small footnote set in 8/10 Times Italic

" 18 |
19 | 20 | 21 | 22 |

ErlGuten. is a system for hiqh quality typesetting, 23 | ErlGuten is free software. ErlGuten aims to produce typographic 24 | quality PDF directly from XML or from a program.

25 | 26 |

The aim of ErlGuten is to produce high quality PDF from a layout 27 | language or from a program. The ErlGuten distribution includes a 28 | programming API, so that Erlang programs can produce PDF - and a 29 | typesetting system for typesetting documents written in XML.

30 | 31 |

The document that you are reading was authored in XML and typeset 32 | by ErlGuten. The name ErlGuten is chosen because the program is 33 | written in Erlang - the Guten part is a reference to 34 | Gutenberg the father of printing.

35 | 36 |

ErlGuten is a system for high quality typesetting, so we take a 37 | great deal of care when formatting text, a large number of 38 | optimizations are performed which improve the quality of the printed 39 | text. Many of these optimizations are usually only found in expensive 40 | professional type-setting programs. We believe that WYSIWYG programs 41 | have destroyed the fine art of typesetting - ErlGuten is a modest 42 | attempt to improve the situation.

43 | 44 |

We have chosen XML as the input language for it's wide appeal. 45 | XML provides only a thin abstraction layer over the typesetting system 46 | - so the adventurous can use the programming interface to ErlGuten - 47 | to directly produce typographic quality PDF in real-time. We expect 48 | this facility to be useful for the dynamic generation of documents 49 | from web-servers.

50 | 51 |

In ErlGuten we take the view that the highest level of 52 | abstraction is the layout of a document - we are very 53 | concerned that the user can specify the exact position of 54 | text on the printed page. At the next level of abstraction we are 55 | concerned with the typefaces that are used to format different regions 56 | of the document.

57 | 58 |

ErlGuten is designed for the production of large and complex 59 | documents with complex layout requirements, like newspapers 60 | or books. In ErlGuten layout, content, and document 61 | management are considered separate issues. Layout is template 62 | based - Content is assumed to be stored as a large number of 63 | documents in a file system or data base, document management is 64 | considered as a mapping operation which takes documents in the 65 | content data base and maps them onto templates to produce hight 66 | quality output.

67 | 68 |

This is normal text, set 30 picas wide in 12/14 Times Roman. 69 | Many different typefaces can be used within a paragraph. 70 | Emphasized text is set in Times-Italic. Hyphenation uses the 71 | TeX hyphenation algorithm. Any of the 35 built-in PDF typefaces with 72 | the same point size can be mixed with a paragraph. The term 73 | {person,"Joe"} is an Erlang term which has been typeset 74 | in 12 point courier. The paragraph justification algorithm 75 | does proper kerning so, for example, the word AWAY is 76 | correctly kerned! - line breaks within a paragraph are selected by a 77 | sophisticated global optimization technique.

78 |
79 |
80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /test/test2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

Erlguten

" 6 |
7 | 8 | 9 |

This is a small footnote set in 6/8 Times Italic. Note that the order 10 | of lines in the output has nothing to do with the order on the page.

" 11 |
12 | 13 | 14 | 15 |

Welcome to ErlGuten. ErlGuten is a system for 16 | typesetting. The aim of ErlGuten is to produce high quality PDF from 17 | a layout language or from a program. The ErlGuten distribution 18 | includes a programming API, so that programs can produce PDF - and a 19 | typesetting system for typesetting documents written in XML.

20 | 21 |

The document that you are reading was authored in XML and typeset 22 | by ErlGuten. The name ErlGuten is chosen because the program is 23 | written in Erlang - the Guten part is a reference to 24 | Gutenberg the father of printing.

25 | 26 |

ErlGuten is a system for high quality typesetting, so we take a 27 | great deal of care when formatting text, a large number of 28 | optimizations are performed which improve the quality of the printed 29 | text. Many of these optimizations are only found in expensive 30 | professional type-setting programs. We believe that WYSIWYG programs 31 | have destroyed the fine art of typesetting - ErlGuten is a modest 32 | attempt to improve the situation.

33 | 34 |

We have chosen XML as the input language for it's wide appeal - 35 | not because of any intrinsic advantages that XML might have. XML 36 | provides only a thin abstraction layer over the typesetting system - 37 | so the adventurous can use the programming interface to ErlGuten - to 38 | directly produce typographic quality PDF in real-time. We expect this 39 | facility to be useful for the dynamic generation of documents from 40 | web-servers.

41 | 42 |

In ErlGuten we take the view that the highest level of 43 | abstraction is the layout of a document - we are very 44 | concerned that the user can specify the exact position of 45 | text on the printed page. At the next level of abstraction we are 46 | concerned with the typefaces that are used to format different regions 47 | of the document - our system makes use of the 14 in-built PDF fonts - 48 | for specialist work type 1 postscript fonts can be used and embedded 49 | in the resulting output. The ErlGuten distribution includes a number 50 | of public domain Type 1 postscript fonts that can be freely included 51 | in any document produced by ErlGuten.

52 | 53 |

Here, paragraphs are composed of 11/13 54 | NewCenturySchlbk-Roman set in a 30 pica measure. 55 | Emphasized text is set in NewCentrySchlbk-Italic. The TeX 56 | hyphenation algorithm is also implemented. I have also some 57 | cursive text and an example of an Erlang term. 58 |

59 | 60 |

61 | The term {person,"Joe"} is an Erlang term. The 62 | variable X, was immediately followed by a comma. The 63 | justification algorithm does proper kerning, which is more 64 | than Microsoft Word can do. AWAY again is correctly kerned! 65 | Erlang terms {like,this} are typeset in 66 | courier.

67 | 68 |

Note, if punctuation is used, it must be attached 69 | to the preceding tagged block, like this, but never 70 | like this, which is incorrect.

71 | 72 |
73 |
74 | -------------------------------------------------------------------------------- /test/tmobile.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richcarl/erlguten/debd6120907e5e18b4bca5fbd5bdd9cb653f282d/test/tmobile.jpg -------------------------------------------------------------------------------- /vsn.mk: -------------------------------------------------------------------------------- 1 | VSN=2.5.1 2 | --------------------------------------------------------------------------------