diff -Nru fntsample-5.3/ChangeLog fntsample-5.4/ChangeLog --- fntsample-5.3/ChangeLog 2018-12-26 07:13:37.000000000 +0000 +++ fntsample-5.4/ChangeLog 2021-08-01 16:18:33.000000000 +0000 @@ -1,3 +1,15 @@ +Changes in version 5.4: + + * Fixed handling of PDF files with already existing outlines + in pdfoutline. + * Added a script for extracting outlines from PDF files + (pdf-extract-outline). + * Pango is always used to draw glyphs, options -p and --use-pango are + accepted but ignored. + * Fixed possible outline corruption in pdfoutline with some versions + of PDF::API2 library (Yifeng Li). + * Various code and build system cleanups. + Changes in version 5.3 * Support reproducible builds with PDF output (Khaled Hosny) diff -Nru fntsample-5.3/cmake/FindIconv.cmake fntsample-5.4/cmake/FindIconv.cmake --- fntsample-5.3/cmake/FindIconv.cmake 2018-12-26 07:13:37.000000000 +0000 +++ fntsample-5.4/cmake/FindIconv.cmake 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ -find_path(Iconv_INCLUDE_DIR - NAMES "iconv.h" - DOC "iconv include directory") -mark_as_advanced(Iconv_INCLUDE_DIR) - -find_library(Iconv_LIBRARY iconv libiconv libiconv-2 c DOC "iconv library") -mark_as_advanced(Iconv_LIBRARY) - -include(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(Iconv - FOUND_VAR Iconv_FOUND - REQUIRED_VARS Iconv_INCLUDE_DIR - FAIL_MESSAGE "Failed to find libiconv") - -if(Iconv_FOUND) - set(Iconv_INCLUDE_DIRS "${Iconv_INCLUDE_DIR}") - if(Iconv_LIBRARY) - set(Iconv_LIBRARIES "${Iconv_LIBRARY}") - else() - unset(Iconv_LIBRARIES) - endif() -endif() diff -Nru fntsample-5.3/CMake/DownloadUnicodeBlocks.cmake fntsample-5.4/CMake/DownloadUnicodeBlocks.cmake --- fntsample-5.3/CMake/DownloadUnicodeBlocks.cmake 1970-01-01 00:00:00.000000000 +0000 +++ fntsample-5.4/CMake/DownloadUnicodeBlocks.cmake 2021-08-01 16:18:33.000000000 +0000 @@ -0,0 +1,36 @@ +function(download_unicode_blocks) + set(default_url "https://unicode.org/Public/UNIDATA/Blocks.txt") + set(UNICODE_BLOCKS "UNICODE_BLOCKS-NOTFOUND" CACHE FILEPATH "Unicode blocks file") + option(CHECK_UNICODE_CERT "Check certificate while downloading Unicode blocks file" ON) + + if(NOT UNICODE_BLOCKS_URL) + set(UNICODE_BLOCKS_URL "${default_url}") + endif() + + if(NOT UNICODE_BLOCKS) + set(download_dest "${CMAKE_BINARY_DIR}/Blocks.txt") + message(STATUS "Downloading ${UNICODE_BLOCKS_URL}...") + file(DOWNLOAD + "${UNICODE_BLOCKS_URL}" + "${download_dest}" + SHOW_PROGRESS + STATUS status + TLS_VERIFY ${CHECK_UNICODE_CERT}) + + list(GET status 0 err) + + if(err) + list(GET status 1 msg) + message(FATAL_ERROR "Download failed (${err}): ${msg}") + endif() + + set(UNICODE_BLOCKS "${download_dest}" CACHE FILEPATH "Unicode blocks file" FORCE) + endif() + + if(NOT EXISTS "${UNICODE_BLOCKS}") + set(UNICODE_BLOCKS "UNICODE_BLOCKS-NOTFOUND" CACHE FILEPATH "Unicode blocks file" FORCE) + message(FATAL_ERROR "Unicode blocks file not found. " + "Use -DUNICODE_BLOCKS= or -DUNICODE_BLOCKS_URL= to specify location of this file.\n" + "Blocks.txt file is available at the Unicode web site: ${default_url}") + endif() +endfunction() diff -Nru fntsample-5.3/CMake/FindXgettext.cmake fntsample-5.4/CMake/FindXgettext.cmake --- fntsample-5.3/CMake/FindXgettext.cmake 1970-01-01 00:00:00.000000000 +0000 +++ fntsample-5.4/CMake/FindXgettext.cmake 2021-08-01 16:18:33.000000000 +0000 @@ -0,0 +1,6 @@ +find_program(XGETTEXT_EXECUTABLE xgettext) + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(Xgettext + REQUIRED_VARS XGETTEXT_EXECUTABLE) diff -Nru fntsample-5.3/CMake/PoFileUtils.cmake fntsample-5.4/CMake/PoFileUtils.cmake --- fntsample-5.3/CMake/PoFileUtils.cmake 1970-01-01 00:00:00.000000000 +0000 +++ fntsample-5.4/CMake/PoFileUtils.cmake 2021-08-01 16:18:33.000000000 +0000 @@ -0,0 +1,100 @@ +include_guard(GLOBAL) + +find_package(Gettext) +find_package(Xgettext) + +if(NOT GETTEXT_FOUND) + message(NOTICE "Translations will not be built.") +endif() + +define_property(TARGET PROPERTY TRANSLATABLE_SOURCES + BRIEF_DOCS "Translatable sources" + FULL_DOCS "List of sources that are used to extract translation templates by xgettext") + +add_custom_target(_updatepot) + +function(add_translations) + set(options) + set(one_value_args POT_FILE) + set(multi_value_args LANGUAGES XGETTEXT_ARGS MSGMERGE_ARGS) + cmake_parse_arguments(PARSE_ARGV 0 ARG + "${options}" "${one_value_args}" "${multi_value_args}") + + set(pot_file "${ARG_POT_FILE}") + get_filename_component(pot_file_path "${pot_file}" ABSOLUTE) + + if(XGETTEXT_FOUND) + add_custom_target(updatepot + COMMAND "${XGETTEXT_EXECUTABLE}" ${ARG_XGETTEXT_ARGS} + $ + -o "${pot_file_path}" + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + COMMENT "Extracting translation templates into ${pot_file}" + COMMAND_EXPAND_LISTS + VERBATIM + ) + else() + message(NOTICE "Updating translation templates will not be possible.") + endif() + + if(NOT GETTEXT_FOUND) + return() + endif() + + if(TARGET updatepot) + add_custom_target(updatepo) + endif() + + add_custom_target(updatepo-only) + + set(gmo_files) + + foreach(lang IN LISTS ARG_LANGUAGES) + set(po_file "${lang}.po") + set(gmo_file "${lang}.gmo") + set(po_file_path "${CMAKE_CURRENT_SOURCE_DIR}/${po_file}") + set(gmo_file_path "${CMAKE_CURRENT_BINARY_DIR}/${gmo_file}") + + add_custom_command(OUTPUT ${gmo_file} + COMMAND ${GETTEXT_MSGFMT_EXECUTABLE} "${po_file_path}" -o "${gmo_file}" + MAIN_DEPENDENCY "${po_file}" + COMMENT "Generating binary catalog ${gmo_file} from ${po_file}" + VERBATIM) + + install(FILES "${gmo_file_path}" + DESTINATION "${CMAKE_INSTALL_LOCALEDIR}/${lang}/LC_MESSAGES/" + RENAME "${CMAKE_PROJECT_NAME}.mo") + + list(APPEND gmo_files "${gmo_file_path}") + + macro(add_po_update_target target main_target) + add_custom_target(${target} + COMMAND "${GETTEXT_MSGMERGE_EXECUTABLE}" ${ARG_MSGMERGE_ARGS} + --update "${po_file}" "${pot_file}" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + DEPENDS ${ARGN} + COMMENT "Merging ${pot_file} into ${po_file}" + VERBATIM + ) + add_dependencies(${main_target} ${target}) + endmacro() + + if(TARGET updatepot) + add_po_update_target(updatepo-${lang} updatepo updatepot) + endif() + add_po_update_target(updatepo-only-${lang} updatepo-only) + endforeach() + + add_custom_target(pofiles ALL DEPENDS ${gmo_files}) +endfunction() + +function(add_translatable_sources) + # Use paths relative to the project directory for source files. This way + # the template file will contain the same file names independen of how + # the build was configured. + foreach(src IN LISTS ARGV) + get_filename_component(src_abs_path "${src}" ABSOLUTE) + file(RELATIVE_PATH src_path "${PROJECT_SOURCE_DIR}" "${src_abs_path}") + set_property(TARGET _updatepot APPEND PROPERTY TRANSLATABLE_SOURCES "${src_path}") + endforeach() +endfunction() diff -Nru fntsample-5.3/CMakeLists.txt fntsample-5.4/CMakeLists.txt --- fntsample-5.3/CMakeLists.txt 2018-12-26 07:13:37.000000000 +0000 +++ fntsample-5.4/CMakeLists.txt 2021-08-01 16:18:33.000000000 +0000 @@ -1,91 +1,43 @@ -cmake_minimum_required(VERSION 3.0) -project(fntsample C) +cmake_minimum_required(VERSION 3.12..3.21) -set(CMAKE_C_STANDARD 99) +project(fntsample + VERSION 5.4 + DESCRIPTION "PDF and PostScript font samples generator" + HOMEPAGE_URL "https://github.com/eugmes/fntsample" + LANGUAGES C) -include(CheckCCompilerFlag) -include(GNUInstallDirs) +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake") -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") +include(GNUInstallDirs) +include(CPack) find_package(PkgConfig REQUIRED) find_package(Intl REQUIRED) -find_package(Gettext REQUIRED) -find_package(Iconv REQUIRED) - -pkg_check_modules(pkgs REQUIRED cairo fontconfig freetype2 glib-2.0 pangocairo>=1.16 pangoft2) - -find_program(AWK NAMES gawk awk mawk) -if(NOT AWK) - message(FATAL_ERROR "awk is required to build the program but was not found.") -endif() - -find_file(UNICODE_BLOCKS Blocks.txt - PATHS /usr/share/unicode - DOC "Unicode blocks file" - NO_DEFAULT_PATH) - -if(NOT UNICODE_BLOCKS) - message(FATAL_ERROR "Unicode blocks file (Blocks.txt) not found. \ -Use -DUNICODE_BLOCKS= to specify location of this file. \ -Blocks.txt file is available at Unicode site: http://unicode.org/Public/UNIDATA/Blocks.txt" -) -endif() - -string(TIMESTAMP DATE "%Y-%m-%d" UTC) -set(CMAKE_REQUIRED_FLAGS -Wl,--as-needed) -check_c_compiler_flag("" HAVE_AS_NEEDED) #empty because we are testing a linker flag -if(HAVE_AS_NEEDED) - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed") +# The target was added in CMake 3.20. +if(NOT TARGET Intl::Intl) + add_library(Intl::Intl INTERFACE IMPORTED GLOBAL) + target_include_directories(Intl::Intl INTERFACE ${Intl_INCLUDE_DIRS}) + target_link_libraries(Intl::Intl INTERFACE ${Intl_LIBRARIES}) endif() -add_compile_options(-Wall -W -Wwrite-strings -Wstrict-prototypes -pedantic) - -configure_file(config.h.in config.h @ONLY) -configure_file(fntsample.1.in fntsample.1 @ONLY) -configure_file(pdfoutline.1.in pdfoutline.1 @ONLY) -configure_file(pdfoutline.pl pdfoutline @ONLY) - -include_directories(fntsample - ${CMAKE_CURRENT_BINARY_DIR} - ${CMAKE_CURRENT_SOURCE_DIR} - ${Intl_INCLUDE_DIRS} - ${Iconv_INCLUDE_DIRS} - ${pkgs_INCLUDE_DIRS} +pkg_check_modules(pkgs REQUIRED IMPORTED_TARGET + cairo>=1.15.4 + fontconfig + freetype2 + glib-2.0 + pangocairo>=1.37.0 + pangoft2>=1.37.0 ) -add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/unicode_blocks.c - COMMAND ${AWK} -f genblocks.awk ${UNICODE_BLOCKS} > ${CMAKE_CURRENT_BINARY_DIR}/unicode_blocks.c - DEPENDS ${UNICODE_BLOCKS} genblocks.awk - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - VERBATIM -) - -set(SOURCES - fntsample.c - read_blocks.c - unicode_blocks.h - ${CMAKE_CURRENT_BINARY_DIR}/unicode_blocks.c - ${CMAKE_CURRENT_BINARY_DIR}/config.h -) +include(DownloadUnicodeBlocks) -add_executable(fntsample ${SOURCES}) +download_unicode_blocks() -target_link_libraries(fntsample - m - ${Intl_LIBRARIES} - ${Iconv_LIBRARIES} - ${pkgs_LIBRARIES} -) - -install(TARGETS fntsample DESTINATION ${CMAKE_INSTALL_BINDIR}) - -install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/pdfoutline" - DESTINATION ${CMAKE_INSTALL_BINDIR}) +string(TIMESTAMP DATE "%Y-%m-%d" UTC) -install(FILES "${PROJECT_BINARY_DIR}/fntsample.1" - "${PROJECT_BINARY_DIR}/pdfoutline.1" - DESTINATION "${CMAKE_INSTALL_MANDIR}/man1") +include(PoFileUtils) +add_subdirectory(src) +add_subdirectory(scripts) add_subdirectory(po) diff -Nru fntsample-5.3/config.h.in fntsample-5.4/config.h.in --- fntsample-5.3/config.h.in 2018-12-26 07:13:37.000000000 +0000 +++ fntsample-5.4/config.h.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -#define PACKAGE "@CMAKE_PROJECT_NAME@" -#define LOCALEDIR "@CMAKE_INSTALL_FULL_LOCALEDIR@" diff -Nru fntsample-5.3/debian/changelog fntsample-5.4/debian/changelog --- fntsample-5.3/debian/changelog 2020-03-23 22:45:40.000000000 +0000 +++ fntsample-5.4/debian/changelog 2021-10-19 20:43:47.000000000 +0000 @@ -1,14 +1,39 @@ -fntsample (5.3-1build2) focal; urgency=medium +fntsample (5.4-3) unstable; urgency=medium - * Rebuild against new unicode-data 13. + [ Євгеній Мещеряков ] + * Trim trailing whitespace. + * Set debhelper-compat version in Build-Depends. + * Set upstream metadata fields: Bug-Database, Bug-Submit, Repository, + Repository-Browse. + * Add debian/salsa-ci.yml. - -- Gianfranco Costamagna Mon, 23 Mar 2020 23:45:40 +0100 + [ Debian Janitor ] + * Remove constraints unnecessary since buster: + + Build-Depends: Drop versioned constraint on cmake, libcairo2-dev and + libpango1.0-dev. -fntsample (5.3-1build1) eoan; urgency=medium + -- Євгеній Мещеряков Tue, 19 Oct 2021 22:43:47 +0200 - * No-change rebuild against unicode-data 12.1.0~pre1-2 +fntsample (5.4-2) unstable; urgency=medium - -- Graham Inggs Thu, 09 May 2019 13:07:20 +0000 + * Upload to unstable + + -- Євгеній Мещеряков Wed, 18 Aug 2021 23:05:26 +0200 + +fntsample (5.4-1) experimental; urgency=medium + + * New upstream release + * Update debian/watch so signature verification works again + * Upload to experimental due to freeze + * Update build-dependencies: + - Don't depend on gawk anymore + - Require libcairo2-dev >= 1.15.4 + - Require libpango1.0-dev >= 1.37 + - Require cmake >= 3.12 + * Use debhelper compat level 13 + * Pass the path to Blocks.txt file to CMake + + -- Євгеній Мещеряков Sun, 01 Aug 2021 22:00:01 +0200 fntsample (5.3-1) unstable; urgency=medium @@ -38,7 +63,7 @@ * New upstream release * debian/watch: update with new source location - * Use cmake to build the package + * Use cmake to build the package * Remove patch reproducible-build.patch - not needed with cmake? * Standards-Version 4.1.1 - no changes required @@ -70,7 +95,7 @@ fntsample (3.2-6) unstable; urgency=medium * Use ${source:Version} variable to generate dependency on unicode-data - (closes: #752007) + (closes: #752007) -- Євгеній Мещеряков Wed, 25 Jun 2014 20:42:21 +0200 @@ -132,7 +157,7 @@ * Rebuild with unicode-data 5.2.0-1 * Standards-Version 3.8.3 — no changes required - * Use sf-redirector in watch file + * Use sf-redirector in watch file -- Євгеній Мещеряков Tue, 13 Oct 2009 21:06:13 +0200 @@ -220,4 +245,3 @@ * Initial release (Closes: #407592) -- Eugeniy Meshcheryakov Sat, 20 Jan 2007 21:53:27 +0100 - diff -Nru fntsample-5.3/debian/compat fntsample-5.4/debian/compat --- fntsample-5.3/debian/compat 2018-12-26 07:48:05.000000000 +0000 +++ fntsample-5.4/debian/compat 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -9 diff -Nru fntsample-5.3/debian/control fntsample-5.4/debian/control --- fntsample-5.3/debian/control 2018-12-26 07:48:05.000000000 +0000 +++ fntsample-5.4/debian/control 2021-10-19 20:43:47.000000000 +0000 @@ -2,8 +2,8 @@ Section: text Priority: optional Maintainer: Євгеній Мещеряков -Build-Depends: debhelper (>= 9~), gawk, libcairo2-dev, unicode-data, libglib2.0-dev, - pkg-config, libfreetype6-dev, libpango1.0-dev (>= 1.16), gettext, cmake (>= 3.0) +Build-Depends: debhelper-compat (= 13), libcairo2-dev, unicode-data, libglib2.0-dev, + pkg-config, libfreetype6-dev, libpango1.0-dev, gettext, cmake Standards-Version: 4.1.1 Vcs-Git: https://salsa.debian.org/debian/fntsample.git Vcs-Browser: https://salsa.debian.org/debian/fntsample diff -Nru fntsample-5.3/debian/rules fntsample-5.4/debian/rules --- fntsample-5.3/debian/rules 2018-12-26 07:48:05.000000000 +0000 +++ fntsample-5.4/debian/rules 2021-10-19 20:43:47.000000000 +0000 @@ -5,5 +5,8 @@ %: dh $@ --buildsystem cmake +override_dh_auto_configure: + dh_auto_configure -- -DUNICODE_BLOCKS:PATH=/usr/share/unicode/Blocks.txt + override_dh_gencontrol: dh_gencontrol -- -VBuilt-Using="`dpkg-query -f'$${source:Package} (= $${source:Version})' -W unicode-data`" diff -Nru fntsample-5.3/debian/salsa-ci.yml fntsample-5.4/debian/salsa-ci.yml --- fntsample-5.3/debian/salsa-ci.yml 1970-01-01 00:00:00.000000000 +0000 +++ fntsample-5.4/debian/salsa-ci.yml 2021-10-19 20:43:47.000000000 +0000 @@ -0,0 +1,4 @@ +--- +include: + project: salsa-ci-team/pipeline + file: recipes/debian.yml diff -Nru fntsample-5.3/debian/upstream/metadata fntsample-5.4/debian/upstream/metadata --- fntsample-5.3/debian/upstream/metadata 1970-01-01 00:00:00.000000000 +0000 +++ fntsample-5.4/debian/upstream/metadata 2021-10-19 20:43:47.000000000 +0000 @@ -0,0 +1,5 @@ +--- +Bug-Database: https://github.com/eugmes/fntsample/issues +Bug-Submit: https://github.com/eugmes/fntsample/issues/new +Repository: https://github.com/eugmes/fntsample.git +Repository-Browse: https://github.com/eugmes/fntsample diff -Nru fntsample-5.3/debian/watch fntsample-5.4/debian/watch --- fntsample-5.3/debian/watch 2018-12-26 07:48:05.000000000 +0000 +++ fntsample-5.4/debian/watch 2021-10-19 20:43:47.000000000 +0000 @@ -1,4 +1,4 @@ version=4 opts=filenamemangle=s/.+\/(\d\S+)\.tar\.gz/fntsample-$1\.tar\.gz/,\ -pgpsigurlmangle=s/archive\/release\/(\d\S+)\.tar\.gz/releases\/download\/release%2F$1\/fntsample-release-$1\.tar\.gz\.asc/ \ +pgpsigurlmangle=s/archive\/refs\/tags\/release\/(\d\S+)\.tar\.gz/releases\/download\/release%2F$1\/fntsample-release-$1\.tar\.gz\.asc/ \ https://github.com/eugmes/fntsample/releases .*/?(\d\S+)\.tar\.gz diff -Nru fntsample-5.3/.editorconfig fntsample-5.4/.editorconfig --- fntsample-5.3/.editorconfig 1970-01-01 00:00:00.000000000 +0000 +++ fntsample-5.4/.editorconfig 2021-08-01 16:18:33.000000000 +0000 @@ -0,0 +1,26 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +trim_trailing_whitespace = true + +[*.{c,h}] +indent_style = space +indent_size = 4 + +[ChangeLog] +indent_style = tab + +[{CMakeLists.txt,*.cmake}] +indent_style = space +indent_size = 2 + +[*.pl] +indent_style = space +indent_size = 4 + +[*.yml] +indent_style = space +indent_size = 2 diff -Nru fntsample-5.3/fntsample.1.in fntsample-5.4/fntsample.1.in --- fntsample-5.3/fntsample.1.in 2018-12-26 07:13:37.000000000 +0000 +++ fntsample-5.4/fntsample.1.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,159 +0,0 @@ -.\" -*- nroff -*- -.TH fntsample 1 @DATE@ -.SH NAME -fntsample \- PDF and PostScript font samples generator -\" macros -.de SAMPLE -.br -.RS -.nf -.nh -.. -.de ESAMPLE -.hy -.fi -.RE -.. -.SH SYNOPSIS -.B fntsample -.BI "[ " OPTIONS " ]" -.BI "\-f " FONT-FILE " \-o " OUTPUT-FILE -.br -.B fntsample \-h -.SH DESCRIPTION -.B fntsample -program can be used to generate font samples that show Unicode coverage -of the font and are similar in appearance to Unicode charts. Samples can be saved -into PDF (default) or PostScript file. -.SH OPTIONS -.B fntsample -supports the following options. -.TP -.BI "\-\-blocks\-file, \-b " BLOCKS-FILE -Read Unicode blocks information from -.IR BLOCKS-FILE . -.TP -.BI "\-\-font\-file, \-f " FONT-FILE -Make samples of -.IR FONT-FILE . -.TP -.BI "\-\-font\-index, \-n " IDX -Font index for \fIFONT-FILE\fP specified using \fB\-\-font\-file\fP option. -Useful for files that contain multiple fonts, like TrueType Collections (.ttc). -By default font with index 0 is used. -.TP -.BI "\-\-output\-file, \-o " OUTPUT-FILE -Write output to -.IR OUTPUT-FILE . -.TP -.BI "\-\-other\-font\-file, \-d " OTHER-FONT -Compare -.I FONT-FILE -with -.IR OTHER-FONT . -Glyphs added to -.I FONT-FILE -will be highlighted. -.TP -.BI "\-\-other\-index, \-m " IDX -Font index for \fIOTHER-FONT\fP specified using \fB\-\-other\-font\-file\fP option. -.TP -.BI "\-\-postscript\-output, \-s" -Use PostScript format for output instead of PDF. -.TP -.BI "\-\-svg, \-g" -Use SVG format for output. -The generated document contains one page. -Use range selection options to specify which. -.TP -.BI "\-\-print\-outline, \-l" -Print document outlines data to standard output. -This data can be used to add outlines (aka bookmarks) to resulting PDF file with \fBpdfoutline\fP program. -.TP -.BI "\-\-write\-outline, \-w" -Write document outlines directly (only in PDF output), requires \fBcairo\fP >= 1.15.4. -.TP -.BI "\-\-use\-pango, \-p" -Use Pango for drawing glyph cells. -Pango applies OpenType layout which may be more bullet proof than just -relaying on the font's cmap. -This especially important for fonts that rely on OpenType layout for -showing even the base forms of the glyphs. -.TP -.BI "\-\-include\-range, \-i " RANGE -Show characters in \fIRANGE\fP. -.TP -.BI "\-\-exclude\-range, \-x " RANGE -Do not show characters in \fIRANGE\fP. -.TP -.BI "\-\-style, \-t \(dq" STYLE ": " VAL "\(dq" -Set \fISTYLE\fP to value \fIVAL\fP. -Run \fBfntsample\fP with option \fB\-\-help\fP to see list of styles and default values. -.TP -.BI "\-\-no\-embed, \-e" -Draw the outlines of the glyphs instead of embedding them in the PDF file. -This can be used when embedding the font is not desired or not allowed. -.TP -.BI "\-\-help, \-h" -Display help text and exit. -.P -Parameter \fIRANGE\fP for \fB\-i\fP and \fB\-x\fP can be given as one integer or a pair of -integers delimited by minus sign (\-). -Integers can be specified in decimal, hexadecimal (0x...) or octal (0...) format. -One integer of a pair can be missing (\-N can be used to specify all characters with codes less -or equal to N, and N\- for all characters with codes greather or equal to N). -Multiple \fB\-i\fP and \fB\-x\fP options can be used. -.SH COLORS -Glyph cells can have one of several background colors. -Meaning of those colors is following: -.TP -.B white -normal glyph present in the font, this includes space glyphs that are usually invisible; -.TP -.B gray -this glyph is defined in Unicode but not present in the font; -.TP -.B blue -this is a control character; -.TP -.B black -this glyph is not defined in Unicode; -.TP -.B yellow -this is a new glyph (only when used with \fB\-d\fP). -.SH ENVIRONMENT -.TP -.B SOURCE_DATE_EPOCH -If $\fBSOURCE_DATE_EPOCH\fP is set, its value is interpreted as Unix timestamp to be used -for creation date of generated PDF files. -This is useful for making builds that use \fBfntsample\fP reproducible. -.SH EXAMPLES -.RI "Make PDF samples for " font.ttf " and write them to file " samples.pdf : -.SAMPLE -fntsample \-f font.ttf \-o samples.pdf -.ESAMPLE -.PP -.RI "Make PDF samples for " font.ttf ", compare it with " oldfont.ttf -.RI "and highlight new glyphs. Write output to file " samples.pdf : -.SAMPLE -fntsample \-f font.ttf \-d oldfont.ttf \-o samples.pdf -.ESAMPLE -.PP -.RI "Make PostScript samples for " font.ttf " and write output to file " samples.ps . -Show only glyphs for characters with codes less or equal to U+04FF but exclude U+0370\-U+03FF: -.SAMPLE -fntsample \-f font.ttf \-s \-o samples.ps \-i \-0x04FF \-x 0x0370\-0x03FF -.ESAMPLE -.PP -.RI "Make PDF samples for " font.ttf " and save output to file " samples.pdf " adding outlines to it:" -.SAMPLE -fntsample \-f font.ttf \-o temp.pdf \-l > outlines.txt -pdfoutline temp.pdf outlines.txt samples.pdf -.ESAMPLE -.SH AUTHOR -Copyright \(co 2007-2017 Eugeniy Meshcheryakov -.br -Homepage: -.SH SEE ALSO -.PP -.BR pdfoutline (1) diff -Nru fntsample-5.3/fntsample.c fntsample-5.4/fntsample.c --- fntsample-5.3/fntsample.c 2018-12-26 07:13:37.000000000 +0000 +++ fntsample-5.4/fntsample.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1114 +0,0 @@ -/* Copyright © 2007-2010 Євгеній Мещеряков - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#include -#include -#include FT_FREETYPE_H -#include FT_SFNT_NAMES_H -#include FT_TRUETYPE_IDS_H -#include FT_TYPE1_TABLES_H -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "unicode_blocks.h" -#include "config.h" - -#if PANGO_VERSION_CHECK(1,37,0) -#define CAN_DRAW_WITH_PANGO -#endif - -#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,15,4) -#define CAN_USE_CAIRO_OUTLINES -#endif - -#define _(str) gettext(str) - -#define A4_WIDTH (8.3*72) -#define A4_HEIGHT (11.7*72) - -#define xmin_border (72.0/1.5) -#define ymin_border (72.0) -#define cell_width ((A4_WIDTH - 2*xmin_border) / 16) -#define cell_height ((A4_HEIGHT - 2*ymin_border) / 16) - -#define CELL_X(x_min, N) ((x_min) + cell_width * ((N) / 16)) -#define CELL_Y(N) (ymin_border + cell_height * ((N) % 16)) - -static struct option longopts[] = { - {"blocks-file", 1, 0, 'b'}, - {"font-file", 1, 0, 'f'}, - {"output-file", 1, 0, 'o'}, - {"help", 0, 0, 'h'}, - {"other-font-file", 1, 0, 'd'}, - {"postscript-output", 0, 0, 's'}, - {"svg", 0, 0, 'g'}, - {"print-outline", 0, 0, 'l'}, - {"write-outline", 0, 0, 'w'}, - {"include-range", 1, 0, 'i'}, - {"exclude-range", 1, 0, 'x'}, - {"style", 1, 0, 't'}, - {"font-index", 1, 0, 'n'}, - {"other-index", 1, 0, 'm'}, - {"no-embed", 0, 0, 'e'}, - {"use-pango", 0, 0, 'p'}, - {0, 0, 0, 0} -}; - -struct range { - uint32_t first; - uint32_t last; - bool include; - struct range *next; -}; - -static const char *font_file_name; -static const char *other_font_file_name; -static const char *output_file_name; -static bool postscript_output; -static bool svg_output; -static bool print_outline; -static bool write_outline; -static bool no_embed; -static bool use_pango; -static struct range *ranges; -static struct range *last_range; -static int font_index; -static int other_index; - -struct fntsample_style { - const char *const name; - const char *const default_val; - char *val; -}; - -static struct fntsample_style styles[] = { - { "header-font", "Sans Bold 12", NULL }, - { "font-name-font", "Serif Bold 12", NULL }, - { "table-numbers-font", "Sans 10", NULL }, - { "cell-numbers-font", "Mono 8", NULL }, - { NULL, NULL, NULL } -}; - -static PangoFontDescription *header_font; -static PangoFontDescription *font_name_font; -static PangoFontDescription *table_numbers_font; -static PangoFontDescription *cell_numbers_font; - -static double cell_label_offset; -static double cell_glyph_bot_offset; -static double glyph_baseline_offset; -static double font_scale; - -static const struct unicode_block *unicode_blocks; - -static void usage(const char *); - -static struct fntsample_style *find_style(const char *name) -{ - struct fntsample_style *style = styles; - - for (; style->name; style++) { - if (!strcmp(name, style->name)) - return style; - } - - return NULL; -} - -static int set_style(const char *name, const char *val) -{ - struct fntsample_style *style; - char *new_val; - - style = find_style(name); - - if (!style) - return -1; - - new_val = strdup(val); - if (!new_val) - return -1; - - if (style->val) - free(style->val); - style->val = new_val; - - return 0; -} - -static const char *get_style(const char *name) -{ - struct fntsample_style *style = find_style(name); - - if (!style) - return NULL; - - return style->val ? style->val : style->default_val; -} - -static int parse_style_string(char *s) -{ - char *n; - - n = strchr(s, ':'); - if (!n) - return -1; - *n++ = '\0'; - return set_style(s, n); -} - -/* - * Update output range. - * - * Returns -1 on error. - */ -static int add_range(char *range, bool include) -{ - struct range *r; - uint32_t first = 0, last = 0xffffffff; - char *minus; - char *endptr; - - minus = strchr(range, '-'); - - if (minus) { - if (minus != range) { - *minus = '\0'; - first = strtoul(range, &endptr, 0); - if (*endptr) - return -1; - } - - if (*(minus + 1)) { - last = strtoul(minus + 1, &endptr, 0); - if (*endptr) - return -1; - } - else if (minus == range) - return -1; - } - else { - first = strtoul(range, &endptr, 0); - if (*endptr) - return -1; - last = first; - } - - if (first > last) - return -1; - - r = malloc(sizeof(*r)); - if (!r) - return -1; - - r->first = first; - r->last = last; - r->include = include; - r->next = NULL; - - if (ranges) - last_range->next = r; - else - ranges = r; - - last_range = r; - - return 0; -} - -/* - * Check if character with the given code belongs - * to output range specified by the user. - */ -static bool in_range(uint32_t c) -{ - bool in = ranges ? (!ranges->include) : 1; - struct range *r; - - for (r = ranges; r; r = r->next) { - if ((c >= r->first) && (c <= r->last)) - in = r->include; - } - return in; -} - -/* - * Get glyph index for the next glyph from the given font face, that - * represents character from output range specified by the user. - * - * Returns character code, updates 'idx'. - * 'idx' can became 0 if there are no more glyphs. - */ -static FT_ULong get_next_char(FT_Face face, FT_ULong charcode, FT_UInt *idx) -{ - FT_ULong rval = charcode; - - do { - rval = FT_Get_Next_Char(face, rval, idx); - } while (*idx && !in_range(rval)); - - return rval; -} - -/* - * Locate first character from the given font face that belongs - * to the user-specified output range. - * - * Returns character code, updates 'idx' with glyph index. - * Glyph index can became 0 if there are no matching glyphs in the font. - */ -static FT_ULong get_first_char(FT_Face face, FT_UInt *idx) -{ - FT_ULong rval; - - rval = FT_Get_First_Char(face, idx); - - if (*idx && !in_range(rval)) - rval = get_next_char(face, rval, idx); - - return rval; -} - -/* - * Create Pango layout for the given text. - * Updates 'r' with text extents. - * Returned layout should be freed using g_object_unref(). - */ -static PangoLayout *layout_text(cairo_t *cr, PangoFontDescription *ftdesc, const char *text, PangoRectangle *r) -{ - PangoLayout *layout; - - layout = pango_cairo_create_layout (cr); - pango_layout_set_font_description(layout, ftdesc); - pango_layout_set_text(layout, text, -1); - pango_layout_get_extents(layout, r, NULL); - - return layout; -} - -static void parse_options(int argc, char * const argv[]) -{ - for (;;) { - int c; - int n; - - c = getopt_long(argc, argv, "b:f:o:hd:sglwi:x:t:n:m:ep", longopts, NULL); - - if (c == -1) - break; - - switch (c) { - case 'b': - if (unicode_blocks) { - fprintf(stderr, _("Unicode blocks file should be given at most once!\n")); - exit(1); - } - - unicode_blocks = read_blocks(optarg, &n); - if (n == 0) { - fprintf(stderr, _("Failed to load any blocks from the blocks file!\n")); - exit(6); - } - break; - case 'f': - if (font_file_name) { - fprintf(stderr, _("Font file name should be given only once!\n")); - exit(1); - } - font_file_name = optarg; - break; - case 'o': - if (output_file_name) { - fprintf(stderr, _("Output file name should be given only once!\n")); - exit(1); - } - output_file_name = optarg; - break; - case 'h': - usage(argv[0]); - exit(0); - break; - case 'd': - if (other_font_file_name) { - fprintf(stderr, _("Font file name should be given only once!\n")); - exit(1); - } - other_font_file_name = optarg; - break; - case 's': - postscript_output = true; - break; - case 'g': - svg_output = true; - break; - case 'l': - print_outline = true; - break; - case 'w': - write_outline = true; -#ifndef CAN_USE_CAIRO_OUTLINES - fprintf(stderr, _("Cairo >= 1.15.4 is required for this option!\n")); - exit(1); -#endif - break; - case 'i': - case 'x': - if (add_range(optarg, c == 'i')) { - usage(argv[0]); - exit(1); - } - break; - case 't': - if (parse_style_string(optarg) == -1) { - usage(argv[0]); - exit(1); - } - break; - case 'n': - font_index = atoi(optarg); - break; - case 'm': - other_index = atoi(optarg); - break; - case 'e': - no_embed = true; - break; - case 'p': -#ifdef CAN_DRAW_WITH_PANGO - use_pango = true; -#else - fprintf(stderr, _("Pango >= 1.37 is required for this option!\n")); - exit(1); -#endif - break; - case '?': - default: - usage(argv[0]); - exit(1); - break; - } - } - if (!font_file_name || !output_file_name) { - usage(argv[0]); - exit(1); - } - if (font_index < 0 || other_index < 0) { - fprintf(stderr, _("Font index should be non-negative!\n")); - exit(1); - } - if (postscript_output && svg_output) { - fprintf(stderr, _("-s and -g cannot be used together!\n")); - exit(1); - } - - if (!unicode_blocks) - unicode_blocks = static_unicode_blocks; -} - -/* - * Locate unicode block that contains given character code. - * Returns this block or NULL if not found. - */ -static const struct unicode_block *get_unicode_block(unsigned long charcode) -{ - const struct unicode_block *block; - - for (block = unicode_blocks; block->name; block++) { - if ((charcode >= block->start) && (charcode <= block->end)) - return block; - } - return NULL; -} - -/* - * Check if the given character code belongs to the given Unicode block. - */ -static bool is_in_block(unsigned long charcode, const struct unicode_block *block) -{ - return ((charcode >= block->start) && (charcode <= block->end)); -} - -/* - * Format and print/write outline information, if requested by the user. - */ -static void outline(cairo_surface_t *surface, int level, int page, const char *text) -{ - if (print_outline) - printf("%d %d %s\n", level, page, text); - -#ifdef CAN_USE_CAIRO_OUTLINES - if (write_outline && cairo_surface_get_type(surface) == CAIRO_SURFACE_TYPE_PDF) { - char *dest = NULL; - int len = 0; - - len = snprintf(dest, 0, "page=%d", page); - dest = malloc(len + 1); - sprintf(dest, "page=%d", page); - - cairo_pdf_surface_add_outline(surface, level, text, dest, CAIRO_PDF_OUTLINE_FLAG_OPEN); - } -#else - (void)surface; -#endif - -} - -/* - * Draw header of a page. - * Header shows font name and current Unicode block. - */ -static void draw_header(cairo_t *cr, const char *face_name, const char *block_name) -{ - PangoLayout *layout; - PangoRectangle r; - - layout = layout_text(cr, font_name_font, face_name, &r); - cairo_move_to(cr, (A4_WIDTH - (double)r.width/PANGO_SCALE)/2.0, 30.0); - pango_cairo_show_layout_line(cr, pango_layout_get_line_readonly(layout, 0)); - g_object_unref(layout); - - layout = layout_text(cr, header_font, block_name, &r); - cairo_move_to(cr, (A4_WIDTH - (double)r.width/PANGO_SCALE)/2.0, 50.0); - pango_cairo_show_layout_line(cr, pango_layout_get_line_readonly(layout, 0)); - g_object_unref(layout); -} - -/* - * Highlight the cell with given coordinates. - * Used to highlight new glyphs. - */ -static void highlight_cell(cairo_t *cr, double x, double y) -{ - cairo_save(cr); - cairo_set_source_rgb(cr, 1.0, 1.0, 0.6); - cairo_rectangle(cr, x, y, cell_width, cell_height); - cairo_fill(cr); - cairo_restore(cr); -} - -/* - * Try to place glyph with the given index at the middle of the cell. - * Changes argument 'glyph' - */ -static void position_glyph(cairo_t *cr, double x, double y, - unsigned long idx, cairo_glyph_t *glyph) -{ - cairo_text_extents_t extents; - - *glyph = (cairo_glyph_t){idx, 0, 0}; - - cairo_glyph_extents(cr, glyph, 1, &extents); - - glyph->x += x + (cell_width - extents.width)/2.0 - extents.x_bearing; - glyph->y += y + glyph_baseline_offset; -} - -/* - * Draw table grid with row and column numbers. - */ -static void draw_grid(cairo_t *cr, unsigned int x_cells, - unsigned long block_start) -{ - unsigned int i; - double x_min = (A4_WIDTH - x_cells * cell_width) / 2; - double x_max = (A4_WIDTH + x_cells * cell_width) / 2; - char buf[17]; - PangoLayout *layout; - PangoRectangle r; - -#define TABLE_H (A4_HEIGHT - ymin_border * 2) - cairo_set_line_width(cr, 1.0); - cairo_rectangle(cr, x_min, ymin_border, x_max - x_min, TABLE_H); - cairo_move_to(cr, x_min, ymin_border); - cairo_line_to(cr, x_min, ymin_border - 15.0); - cairo_move_to(cr, x_max, ymin_border); - cairo_line_to(cr, x_max, ymin_border - 15.0); - cairo_stroke(cr); - - cairo_set_line_width(cr, 0.5); - /* draw horizontal lines */ - for (i = 1; i < 16; i++) { - cairo_move_to(cr, x_min, 72.0 + i * TABLE_H/16); - cairo_line_to(cr, x_max, 72.0 + i * TABLE_H/16); - } - - /* draw vertical lines */ - for (i = 1; i < x_cells; i++) { - cairo_move_to(cr, x_min + i * cell_width, ymin_border); - cairo_line_to(cr, x_min + i * cell_width, A4_HEIGHT - ymin_border); - } - cairo_stroke(cr); - - /* draw glyph numbers */ - buf[1] = '\0'; -#define hexdigs "0123456789ABCDEF" - - for (i = 0; i < 16; i++) { - buf[0] = hexdigs[i]; - layout = layout_text(cr, table_numbers_font, buf, &r); - cairo_move_to(cr, x_min - (double)PANGO_RBEARING(r)/PANGO_SCALE - 5.0, - 72.0 + (i+0.5) * TABLE_H/16 + (double)PANGO_DESCENT(r)/PANGO_SCALE/2); - pango_cairo_show_layout_line(cr, pango_layout_get_line_readonly(layout, 0)); - cairo_move_to(cr, x_min + x_cells * cell_width + 5.0, - 72.0 + (i+0.5) * TABLE_H/16 + (double)PANGO_DESCENT(r)/PANGO_SCALE/2); - pango_cairo_show_layout_line(cr, pango_layout_get_line_readonly(layout, 0)); - g_object_unref(layout); - } - - for (i = 0; i < x_cells; i++) { - snprintf(buf, sizeof(buf), "%03lX", block_start / 16 + i); - layout = layout_text(cr, table_numbers_font, buf, &r); - cairo_move_to(cr, x_min + i*cell_width + (cell_width - (double)r.width/PANGO_SCALE)/2, - ymin_border - 5.0); - pango_cairo_show_layout_line(cr, pango_layout_get_line_readonly(layout, 0)); - g_object_unref(layout); - } -} - -/* - * Fill empty cell. Color of the fill depends on the character properties. - */ -static void fill_empty_cell(cairo_t *cr, double x, double y, unsigned long charcode) -{ - cairo_save(cr); - if (g_unichar_isdefined(charcode)) { - if (g_unichar_iscntrl(charcode)) - cairo_set_source_rgb(cr, 0.0, 0.0, 0.5); - else - cairo_set_source_rgb(cr, 0.5, 0.5, 0.5); - } - cairo_rectangle(cr, x, y, cell_width, cell_height); - cairo_fill(cr); - cairo_restore(cr); -} - -/* - * Draw label with character code. - */ -static void draw_charcode(cairo_t *cr, double x, double y, FT_ULong charcode) -{ - char buf[9]; - PangoLayout *layout; - PangoRectangle r; - - snprintf(buf, sizeof(buf), "%04lX", charcode); - layout = layout_text(cr, cell_numbers_font, buf, &r); - cairo_move_to(cr, x + (cell_width - (double)r.width/PANGO_SCALE)/2.0, y + cell_height - cell_label_offset); - pango_cairo_show_layout_line(cr, pango_layout_get_line_readonly(layout, 0)); - g_object_unref(layout); -} - -/* - * Draws tables for all characters in the given Unicode block. - * Use font described by face and ft_face. Start from character - * with given charcode (it should belong to the given Unicode - * block). After return 'charcode' equals the last character code - * of the block. - * - * Returns number of pages drawn. - */ -static int draw_unicode_block(cairo_t *cr, cairo_scaled_font_t *font, - FT_Face ft_face, const char *fontname, unsigned long *charcode, - const struct unicode_block *block, FT_Face ft_other_face) -{ - FT_UInt idx; - unsigned long prev_charcode; - unsigned long prev_cell; - int npages = 0; - -#ifdef CAN_DRAW_WITH_PANGO - FcConfig *fc_config = NULL; - PangoFontMap *fontmap = NULL; - PangoContext *context = NULL; - PangoLayout *layout = NULL; - PangoFontDescription *font_desc = NULL; - - if (use_pango) { - fc_config = FcConfigCreate(); - FcConfigAppFontAddFile(fc_config, (FcChar8 *) font_file_name); - fontmap = pango_cairo_font_map_new_for_font_type(CAIRO_FONT_TYPE_FT); - pango_fc_font_map_set_config(PANGO_FC_FONT_MAP(fontmap), fc_config); - context = pango_font_map_create_context(fontmap); - pango_cairo_update_context (cr, context); - - font_desc = pango_font_description_new(); - layout = pango_layout_new (context); - pango_layout_set_font_description(layout, font_desc); - pango_layout_set_width(layout, cell_width * PANGO_SCALE); - pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER); - } -#endif - - idx = FT_Get_Char_Index(ft_face, *charcode); - - do { - unsigned long offset = ((*charcode - block->start) / 0x100) * 0x100; - unsigned long tbl_start = block->start + offset; - unsigned long tbl_end = tbl_start + 0xFF > block->end ? - block->end + 1 : tbl_start + 0x100; - unsigned int rows = (tbl_end - tbl_start) / 16; - double x_min = (A4_WIDTH - rows * cell_width) / 2; - unsigned long i; - bool filled_cells[256]; /* 16x16 glyphs max */ - - /* XXX WARNING: not reentrant! */ - static cairo_glyph_t glyphs[256]; - unsigned int nglyphs = 0; - - cairo_save(cr); - draw_header(cr, fontname, block->name); - prev_cell = tbl_start - 1; - - memset(filled_cells, '\0', sizeof(filled_cells)); - - if (!use_pango) - cairo_set_scaled_font(cr, font); - - /* - * Fill empty cells and calculate coordinates of the glyphs. - * Also highlight cells if needed. - */ - do { - /* the current glyph position in the table */ - int charpos = *charcode - tbl_start; - - /* fill empty cells before the current glyph */ - for (i = prev_cell + 1; i < *charcode; i++) { - int pos = i - tbl_start; - fill_empty_cell(cr, CELL_X(x_min, pos), CELL_Y(pos), i); - } - - /* if it is new glyph - highlight the cell */ - if (ft_other_face && !FT_Get_Char_Index(ft_other_face, *charcode)) - highlight_cell(cr, CELL_X(x_min, charpos), CELL_Y(charpos)); - - /* For now just position glyphs. They will be shown later, - * to make output more efficient. */ - if (!use_pango) { - position_glyph(cr, CELL_X(x_min, charpos), CELL_Y(charpos), - idx, &glyphs[nglyphs++]); - } else { -#ifdef CAN_DRAW_WITH_PANGO - char buf[9]; - gint len; - double baseline; - - len = g_unichar_to_utf8((gunichar) *charcode, buf); - pango_layout_set_text(layout, buf, len); - - baseline = pango_layout_get_baseline (layout) / PANGO_SCALE; - cairo_move_to(cr, CELL_X(x_min, charpos), CELL_Y(charpos) + glyph_baseline_offset - baseline); - if (no_embed) - pango_cairo_layout_path(cr, layout); - else - pango_cairo_show_layout(cr, layout); -#endif - } - - filled_cells[charpos] = true; - - prev_charcode = *charcode; - prev_cell = *charcode; - *charcode = get_next_char(ft_face, *charcode, &idx); - } while (idx && (*charcode < tbl_end) && is_in_block(*charcode, block)); - - /* Fill remaining empty cells */ - for (i = prev_cell + 1; i < tbl_end; i++) { - int pos = i - tbl_start; - fill_empty_cell(cr, CELL_X(x_min, pos), CELL_Y(pos), i); - } - - /* Show previously positioned glyphs */ - if (!use_pango) { - if (no_embed) { - cairo_save(cr); - cairo_glyph_path (cr, glyphs, nglyphs); - cairo_fill(cr); - cairo_restore(cr); - } else { - cairo_show_glyphs(cr, glyphs, nglyphs); - } - } - - for (i = 0; i < tbl_end - tbl_start; i++) - if (filled_cells[i]) - draw_charcode(cr, CELL_X(x_min, i), CELL_Y(i), - i + tbl_start); - - draw_grid(cr, rows, tbl_start); - npages++; - cairo_show_page(cr); - cairo_restore(cr); - } while (idx && is_in_block(*charcode, block)); - -#ifdef CAN_DRAW_WITH_PANGO - if (use_pango) { - g_object_unref(layout); - g_object_unref(context); - g_object_unref(fontmap); - pango_font_description_free(font_desc); - FcConfigDestroy(fc_config); - } -#endif - - *charcode = prev_charcode; - return npages; -} - -/* - * The main drawing function. - */ -static void draw_glyphs(cairo_t *cr, cairo_scaled_font_t *font, FT_Face ft_face, - const char *fontname, FT_Face ft_other_face) -{ - FT_ULong charcode; - FT_UInt idx; - const struct unicode_block *block; - int pageno = 1; - cairo_surface_t *surface = cairo_get_target(cr); - - outline(surface, 0, pageno, fontname); - - charcode = get_first_char(ft_face, &idx); - - while (idx) { - block = get_unicode_block(charcode); - if (block) { - int npages; - outline(surface, 1, pageno, block->name); - npages = draw_unicode_block(cr, font, ft_face, fontname, - &charcode, block, ft_other_face); - pageno += npages; - } - charcode = get_next_char(ft_face, charcode, &idx); - } -} - -/* - * Print usage instructions and default values for styles - */ -static void usage(const char *cmd) -{ - const struct fntsample_style *style; - - fprintf(stderr, _("Usage: %s [ OPTIONS ] -f FONT-FILE -o OUTPUT-FILE\n" - " %s -h\n\n") , cmd, cmd); - fprintf(stderr, _("Options:\n" - " --blocks-file, -b BLOCKS-FILE Read Unicode blocks information from BLOCKS-FILE\n" - " --font-file, -f FONT-FILE Create samples of FONT-FILE\n" - " --font-index, -n IDX Font index in FONT-FILE\n" - " --output-file, -o OUTPUT-FILE Save samples to OUTPUT-FILE\n" - " --help, -h Show this information message and exit\n" - " --other-font-file, -d OTHER-FONT Compare FONT-FILE with OTHER-FONT and highlight added glyphs\n" - " --other-index, -m IDX Font index in OTHER-FONT\n" - " --postscript-output, -s Use PostScript format for output instead of PDF\n" - " --svg, -g Use SVG format for output\n" - " --print-outline, -l Print document outlines data to standard output\n" - " --write-outline, -w Write document outlines (only in PDF output)\n" - " --no-embed, -e Don't embed the font in the output file, draw the glyphs instead\n" - " --use-pango -p Use Pango for drawing glyph cells\n" - " --include-range, -i RANGE Show characters in RANGE\n" - " --exclude-range, -x RANGE Do not show characters in RANGE\n" - " --style, -t \"STYLE: VAL\" Set STYLE to value VAL\n")); - fprintf(stderr, _("\nSupported styles (and default values):\n")); - for (style = styles; style->name; style++) - fprintf(stderr, "\t%s (%s)\n", style->name, style->default_val); -} - -/* - * Try to get font name for a given font face. - * Returned name should be free()'d after use. - * If function cannot allocate memory, it terminates the program. - */ -static const char *get_font_name(FT_Face face) -{ - FT_Error error; - char *fontname = NULL; - - /* try SFNT format */ - unsigned num_names = FT_Get_Sfnt_Name_Count(face); - iconv_t u16to8 = iconv_open("UTF-8", "UTF-16BE"); - for (unsigned i = 0; i < num_names; i++) { - FT_SfntName name; - - error = FT_Get_Sfnt_Name(face, i, &name); - if (error) - continue; - - if (name.name_id == TT_NAME_ID_FULL_NAME && - name.platform_id == TT_PLATFORM_MICROSOFT && - name.encoding_id == TT_MS_ID_UNICODE_CS) { - fontname = malloc(name.string_len * 2 + 1); - char *bufptr = fontname; - size_t inbytes = name.string_len; - size_t outbytes = name.string_len * 2; - if (iconv(u16to8, (char**)&name.string, &inbytes, &bufptr, &outbytes) == (size_t)-1) - continue; - *bufptr = '\0'; - } - } - iconv_close(u16to8); - if (fontname) - return fontname; - - /* try Type1 format */ - PS_FontInfoRec fontinfo; - - error = FT_Get_PS_Font_Info(face, &fontinfo); - if (!error) { - if (fontinfo.full_name) { - fontname = strdup(fontinfo.full_name); - if (!fontname) { - perror("strdup"); - exit(1); - } - return fontname; - } - } - - /* fallback */ - const char *family_name = face->family_name ? face->family_name : "Unknown"; - const char *style_name = face->style_name ? face->style_name : ""; - size_t len = strlen(family_name) + strlen(style_name) + 1/* for space */; - - fontname = malloc(len + 1); - if (!fontname) { - perror("malloc"); - exit(1); - } - - sprintf(fontname, "%s %s", family_name, style_name); - return fontname; -} - -/* - * Initialize fonts used to print table heders and character codes. - */ -static void init_pango_fonts(void) -{ - /* FIXME is this correct? */ - PangoCairoFontMap *map = (PangoCairoFontMap *) pango_cairo_font_map_get_default(); - - pango_cairo_font_map_set_resolution(map, 72.0); - - header_font = pango_font_description_from_string(get_style("header-font")); - font_name_font = pango_font_description_from_string(get_style("font-name-font")); - table_numbers_font = pango_font_description_from_string(get_style("table-numbers-font")); - cell_numbers_font = pango_font_description_from_string(get_style("cell-numbers-font")); -} - -/* - * Calculate various offsets. - */ -static void calculate_offsets(cairo_t *cr) -{ - PangoRectangle extents; - /* Assume that vertical extents does not depend on actual text */ - PangoLayout *l = layout_text(cr, cell_numbers_font, "0123456789ABCDEF", &extents); - g_object_unref(l); - /* Unsolved mistery of pango's font metrics.... */ - double digits_ascent = pango_units_to_double(PANGO_DESCENT(extents)); - double digits_descent = -pango_units_to_double(PANGO_ASCENT(extents)); - - cell_label_offset = digits_descent + 2; - cell_glyph_bot_offset = cell_label_offset + digits_ascent + 2; -} - -/* - * Create cairo scaled font with the best size (hopefuly...) - */ -static cairo_scaled_font_t *create_default_font(FT_Face ft_face) -{ - cairo_font_face_t *cr_face = cairo_ft_font_face_create_for_ft_face(ft_face, 0); - cairo_matrix_t font_matrix; - cairo_matrix_t ctm; - cairo_font_options_t *options = cairo_font_options_create(); - cairo_scaled_font_t *cr_font; - cairo_font_extents_t extents; - - - /* First create font with size 1 and measure it */ - cairo_matrix_init_identity(&font_matrix); - cairo_matrix_init_identity(&ctm); - /* Turn off rounding, so we can get real metrics */ - cairo_font_options_set_hint_metrics(options, CAIRO_HINT_METRICS_OFF); - cr_font = cairo_scaled_font_create(cr_face, &font_matrix, &ctm, options); - cairo_scaled_font_extents(cr_font, &extents); - - /* Use some magic to find the best font size... */ - double tgt_size = cell_height - cell_glyph_bot_offset - 2; - if (tgt_size <= 0) { - fprintf(stderr, _("Not enough space for rendering glyphs. Make cell font smaller.\n")); - exit(5); - } - double act_size = extents.ascent + extents.descent; - if (act_size <= 0) { - fprintf(stderr, _("The font has strange metrics: ascent + descent = %g\n"), act_size); - exit(5); - } - font_scale = tgt_size / act_size; - if (font_scale > 1) - font_scale = trunc(font_scale); // just to make numbers nicer - if (font_scale > 20) - font_scale = 20; // Do not make font larger than in previous versions - - cairo_scaled_font_destroy(cr_font); - - /* Create the font once again, but this time scaled */ - cairo_matrix_init_scale(&font_matrix, font_scale, font_scale); - cr_font = cairo_scaled_font_create(cr_face, &font_matrix, &ctm, options); - cairo_scaled_font_extents(cr_font, &extents); - glyph_baseline_offset = (tgt_size - (extents.ascent + extents.descent)) / 2 + 2 + extents.ascent; - return cr_font; -} - -int main(int argc, char **argv) -{ - cairo_surface_t *surface; - cairo_t *cr; - FT_Error error; - FT_Library library; - FT_Face face, other_face = NULL; - const char *fontname; /* full name of the font */ - cairo_status_t cr_status; - cairo_scaled_font_t *cr_font; - - setlocale(LC_ALL, ""); - bindtextdomain(PACKAGE, LOCALEDIR); - textdomain(PACKAGE); - - parse_options(argc, argv); - - error = FT_Init_FreeType(&library); - if (error) { - /* TRANSLATORS: 'freetype' is a name of a library, and should be left untranslated */ - fprintf(stderr, _("%s: freetype error\n"), argv[0]); - exit(3); - } - - error = FT_New_Face(library, font_file_name, font_index, &face); - if (error) { - fprintf(stderr, _("%s: failed to open font file %s\n"), argv[0], font_file_name); - exit(4); - } - - fontname = get_font_name(face); - - if (other_font_file_name) { - error = FT_New_Face(library, other_font_file_name, other_index, &other_face); - if (error) { - fprintf(stderr, _("%s: failed to create new font face\n"), argv[0]); - exit(4); - } - } - - if (postscript_output) - surface = cairo_ps_surface_create(output_file_name, A4_WIDTH, A4_HEIGHT); - else if (svg_output) - surface = cairo_svg_surface_create(output_file_name, A4_WIDTH, A4_HEIGHT); - else { - surface = cairo_pdf_surface_create(output_file_name, A4_WIDTH, A4_HEIGHT); /* A4 paper */ - -#ifdef CAN_USE_CAIRO_OUTLINES - char buffer[25]; - char *endptr; - char *source_date_epoch; - unsigned long long epoch; - time_t now; - struct tm *build_time; - source_date_epoch = getenv("SOURCE_DATE_EPOCH"); - if (source_date_epoch) { - errno = 0; - epoch = strtoull(source_date_epoch, &endptr, 10); - if ((errno == ERANGE && (epoch == ULLONG_MAX || epoch == 0)) || (errno != 0 && epoch == 0)) { - fprintf(stderr, _("Environment variable $SOURCE_DATE_EPOCH: strtoull: %s\n"), - strerror(errno)); - exit(1); - } - if (endptr == source_date_epoch) { - fprintf(stderr, _("Environment variable $SOURCE_DATE_EPOCH: No digits were found: %s\n"), - endptr); - exit(1); - } - if (*endptr != '\0') { - fprintf(stderr, _("Environment variable $SOURCE_DATE_EPOCH: Trailing garbage: %s\n"), - endptr); - exit(1); - } - if (epoch > ULONG_MAX) { - fprintf(stderr, _("Environment variable $SOURCE_DATE_EPOCH: must be <= %lu but saw: %llu\n"), - ULONG_MAX, epoch); - exit(1); - } - now = (time_t)epoch; - build_time = gmtime(&now); - strftime(buffer, 25, "%Y-%m-%dT%H:%M:%S%z", build_time); - cairo_pdf_surface_set_metadata(surface, - CAIRO_PDF_METADATA_CREATE_DATE, - buffer); - } -#endif - } - - cr_status = cairo_surface_status(surface); - if (cr_status != CAIRO_STATUS_SUCCESS) { - /* TRANSLATORS: 'cairo' is a name of a library, and should be left untranslated */ - fprintf(stderr, _("%s: failed to create cairo surface: %s\n"), - argv[0], cairo_status_to_string(cr_status)); - exit(1); - } - - cr = cairo_create(surface); - cr_status = cairo_status(cr); - if (cr_status != CAIRO_STATUS_SUCCESS) { - fprintf(stderr, _("%s: cairo_create failed: %s\n"), - argv[0], cairo_status_to_string(cr_status)); - exit(1); - } - - cairo_surface_destroy(surface); - - init_pango_fonts(); - calculate_offsets(cr); - cr_font = create_default_font(face); - cr_status = cairo_scaled_font_status(cr_font); - if (cr_status != CAIRO_STATUS_SUCCESS) { - fprintf(stderr, _("%s: failed to create scaled font: %s\n"), - argv[0], cairo_status_to_string(cr_status)); - exit(1); - } - - cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); - draw_glyphs(cr, cr_font, face, fontname, other_face); - cairo_destroy(cr); - return 0; -} diff -Nru fntsample-5.3/genblocks.awk fntsample-5.4/genblocks.awk --- fntsample-5.3/genblocks.awk 2018-12-26 07:13:37.000000000 +0000 +++ fntsample-5.4/genblocks.awk 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ -# This file is in public domain -# Author: Eugeniy Meshcheryakov - -BEGIN { - print "#include \"unicode_blocks.h\"" - print "" - print "const struct unicode_block static_unicode_blocks[] = {" -} - -/^[^#]/ { - if (split($0, a, /\.\.|; /) == 3) { - # remove any ^M characters, like ones in dos line breaks - # NOTE: gsub() is used because mawk does not support gensub() - gsub(/\r/, "", a[3]) - print "\t{0x" a[1] ", 0x" a[2] ", \"" a[3] "\"},"; - } -} - -END { - print "\t{0, 0, NULL}," - print "};" -} diff -Nru fntsample-5.3/.github/workflows/ci.yml fntsample-5.4/.github/workflows/ci.yml --- fntsample-5.3/.github/workflows/ci.yml 1970-01-01 00:00:00.000000000 +0000 +++ fntsample-5.4/.github/workflows/ci.yml 2021-08-01 16:18:33.000000000 +0000 @@ -0,0 +1,50 @@ +name: CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +env: + BUILD_TYPE: Release + +jobs: + build: + strategy: + matrix: + os: + - ubuntu-latest + - macos-latest + include: + - os: ubuntu-latest + blocks: -DUNICODE_BLOCKS=/usr/share/unicode/Blocks.txt + - os: macos-latest + env: + - CMAKE_PREFIX_PATH: /usr/local/opt/gettext + + runs-on: ${{matrix.os}} + + steps: + - uses: actions/checkout@v2 + + - name: Install APT Packages + if: ${{ matrix.os == 'ubuntu-latest' }} + run: > + sudo apt update && + sudo apt install cmake gettext libcairo2-dev libglib2.0-dev libfreetype6-dev libpango1.0-dev ninja-build pkg-config unicode-data + + - name: Install Homebrew Packages + if: ${{ matrix.os == 'macos-latest' }} + run: > + brew update && + brew install cairo cmake fontconfig freetype gettext glib pango ninja pkg-config + + - name: Configure + run: cmake -GNinja -B${{ github.workspace }}/build -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} ${{ matrix.blocks }} + + - name: Build + run: cmake --build ${{ github.workspace }}/build + + - name: Test + run: ${{ github.workspace }}/build/src/fntsample --help diff -Nru fntsample-5.3/.gitignore fntsample-5.4/.gitignore --- fntsample-5.3/.gitignore 1970-01-01 00:00:00.000000000 +0000 +++ fntsample-5.4/.gitignore 2021-08-01 16:18:33.000000000 +0000 @@ -0,0 +1,2 @@ +/build/ +.vscode diff -Nru fntsample-5.3/Messages.sh fntsample-5.4/Messages.sh --- fntsample-5.3/Messages.sh 2018-12-26 07:13:37.000000000 +0000 +++ fntsample-5.4/Messages.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -#! /bin/sh -# Run this file to update message catalogs under po/ -# -# This file is in the public domain. - -set -e - -XGETTEXT="xgettext --keyword=_ --keyword=N_ --keyword=__ --keyword=__x --default-domain=fntsample --add-comments=TRANSLATORS: --foreign-user --package-name fntsample --msgid-bugs-address=eugen@debian.org" -MSGMERGE="msgmerge --backup=none --update --verbose" - -${XGETTEXT} fntsample.c read_blocks.c pdfoutline.pl -o po/fntsample.pot - -for po_file in po/*.po -do - ${MSGMERGE} ${po_file} po/fntsample.pot -done diff -Nru fntsample-5.3/pdfoutline.1.in fntsample-5.4/pdfoutline.1.in --- fntsample-5.3/pdfoutline.1.in 2018-12-26 07:13:37.000000000 +0000 +++ fntsample-5.4/pdfoutline.1.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,63 +0,0 @@ -.\" -*- nroff -*- -.TH pdfoutline 1 @DATE@ -.SH NAME -pdfoutline \- add outlines (aka bookmarks) to PDF files -\" macros -.de SAMPLE -.br -.RS -.nf -.nh -.. -.de ESAMPLE -.hy -.fi -.RE -.. -.SH SYNOPSIS -.B pdfoutline -.I input.pdf outlines.txt output.pdf -.SH DESCRIPTION -\fBpdfoutline\fP reads input file given as first argument, adds outlines from text file given -as second argument, and saves result to file with name given as third argument. -.P -File with outlines information should consist of lines in the following format: -.SAMPLE -\fI\fP \fI\fP Outline text -.ESAMPLE -.P -\fI\fP and \fI\fP should be integers. -Each field should be separated by exactly one space symbol. -All values for \fI\fP should be greater or equal than that of the first line. -Page numeration starts with 1. -.P -Outlines file can contain comments that start with # in first column. -Comments and empty lines are ignored. -The text is expected to be in UTF-8 encoding. -.SH OPTIONS -.B pdfoutline -accepts no options. -.SH EXAMPLES -Here is example of outlines data file: -.SAMPLE -0 1 Document title -1 1 Chapter 1 -2 1 Chapter 1.1 -2 2 Chapter 1.2 -1 3 Chapter 2 -.ESAMPLE -.P -Using this file will result in outlines like the following: -.SAMPLE -Document title -+-Chapter 1 -| +-Chapter 1.1 -| +-Chapter 1.2 -+-Chapter 2 -.ESAMPLE -.SH AUTHOR -.B pdfoutline -author is Eugeniy Meshcheryakov -.br -\fBpdfoutline\fP is part of \fBfntsample\fP and can be downoaded from -. diff -Nru fntsample-5.3/pdfoutline.pl fntsample-5.4/pdfoutline.pl --- fntsample-5.3/pdfoutline.pl 2018-12-26 07:13:37.000000000 +0000 +++ fntsample-5.4/pdfoutline.pl 1970-01-01 00:00:00.000000000 +0000 @@ -1,118 +0,0 @@ -#! /usr/bin/perl -w -# This file is in public domain -# Author: Eugeniy Meshcheryakov -# -# This program adds outlines to pdf files. -# Usage: pdfoutline input.pdf outline.txt out.pdf -# -# File given as second argument should contain outline information in -# form: -# -# Some text -# -# where and are integers. Values for should be greater -# or equal than that of first line. Page numeration starts with 1. -# -# Outlines file can contain comments that start with # in first column. Comments -# and empty lines are ignored. -# -# Example file: -# 0 1 Document title -# 1 1 Chapter 1 -# 2 1 Chapter 1.1 -# 2 2 Chapter 1.2 -# 1 3 Chapter 2 -# -# This file will result in outline like the following: -# -# Document title -# +-Chapter 1 -# | +-Chapter 1.1 -# | +-Chapter 1.2 -# +-Chapter 2 - -use strict; -use PDF::API2; -use Locale::TextDomain('@CMAKE_PROJECT_NAME@', '@CMAKE_INSTALL_FULL_LOCALEDIR@'); -use POSIX qw(:locale_h); -use Encode qw(encode); -use subs qw(add_outlines); - -sub usage() { - printf(__"Usage: %s input.pdf outline.txt out.pdf\n", $0); -} - -# get first non-empty non-comment line -sub get_line($) { - my $F = shift; - my $line; - - while ($line = <$F>) { - chomp $line; - # skip comments ... - next if $line =~ /^#/; - # ... and empty lines - next if $line =~ /^$/; - last; - } - return $line; -} - -# Encode string to UTF-16BE with BOM if it contains non-ASCII characters -sub encode_pdf_text($) { - my $str = shift; - - if ($str !~ /[^[:ascii:]]/) { - return $str; - } else { - return encode("UTF-16", $str); - } -} - -sub add_outlines($$$$) { - my $pdf = shift; - my $parent = shift; - my $line = shift; - my $F = shift; - my $cur_outline; - - my ($level) = split / /, $line; - - MAINLOOP: while ($line) { - my ($new_level, $page, $text) = split / /, $line, 3; - - if ($new_level > $level) { - $line = add_outlines($pdf, $cur_outline, $line, $F); - next MAINLOOP; - } - elsif ($new_level < $level) { - return $line; - } - else { - $cur_outline = $parent->outline; - $cur_outline->title(encode_pdf_text($text)); - # FIXME it should be posible to make it easier - my $pdfpage = $pdf->{pagestack}->[$page - 1]; - $cur_outline->dest($pdfpage); - } - - $line = get_line($F); - } -} - -setlocale(LC_ALL, ''); - -if ($#ARGV != 2) { - usage; - exit 1; -} - -my $inputfile = $ARGV[0]; -my $outlinefile = $ARGV[1]; -my $outputfile = $ARGV[2]; -my $pdf = PDF::API2->open($inputfile); -open(OUTLINE, "<:encoding(UTF-8)", $outlinefile) or die __x("Cannot open outline file '{outlinefile}'", outlinefile => $outlinefile); -my $line = get_line(*OUTLINE); -add_outlines($pdf, $pdf->outlines, $line, *OUTLINE) if $line; -$pdf->saveas($outputfile); -exit 0; diff -Nru fntsample-5.3/po/CMakeLists.txt fntsample-5.4/po/CMakeLists.txt --- fntsample-5.3/po/CMakeLists.txt 2018-12-26 07:13:37.000000000 +0000 +++ fntsample-5.4/po/CMakeLists.txt 2021-08-01 16:18:33.000000000 +0000 @@ -1,26 +1,13 @@ -set(LANGUAGES uk) - -add_custom_target(pofiles) - -function(process_po_files _langs) - foreach(_lang ${_langs}) - set(_po_file "${CMAKE_CURRENT_SOURCE_DIR}/${_lang}.po") - set(_gmo_file "${CMAKE_CURRENT_BINARY_DIR}/${_lang}.gmo") - set(_gmo_file_tgt "${_lang}.gmo") - - add_custom_command(OUTPUT ${_gmo_file} - COMMAND ${GETTEXT_MSGFMT_EXECUTABLE} ${_po_file} -o ${_gmo_file} - DEPENDS ${_po_file} - COMMENT "Compiling ${_lang}.po to ${_lang}.gmo" - VERBATIM) - - install(FILES ${_gmo_file} - DESTINATION "${CMAKE_INSTALL_LOCALEDIR}/${_lang}/LC_MESSAGES/" - RENAME "${CMAKE_PROJECT_NAME}.mo") - - add_custom_target(${_gmo_file_tgt} ALL DEPENDS ${_gmo_file}) - add_dependencies(pofiles ${_gmo_file_tgt}) - endforeach() -endfunction() - -process_po_files(${LANGUAGES}) +set(XGETTEXT_ARGS + --keyword=_ --keyword=N_ --keyword=__ --keyword=__x + --default-domain=${CMAKE_PROJECT_NAME} + --add-comments=TRANSLATORS: --foreign-user + --package-name ${CMAKE_PROJECT_NAME} + --msgid-bugs-address=eugen@debian.org) + +add_translations( + POT_FILE fntsample.pot + LANGUAGES uk + XGETTEXT_ARGS ${XGETTEXT_ARGS} + MSGMERGE_ARGS --backup=none --verbose +) diff -Nru fntsample-5.3/po/fntsample.pot fntsample-5.4/po/fntsample.pot --- fntsample-5.3/po/fntsample.pot 2018-12-26 07:13:37.000000000 +0000 +++ fntsample-5.4/po/fntsample.pot 2021-08-01 16:18:33.000000000 +0000 @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: fntsample\n" "Report-Msgid-Bugs-To: eugen@debian.org\n" -"POT-Creation-Date: 2017-10-22 01:32+0200\n" +"POT-Creation-Date: 2021-08-01 16:57+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -16,47 +16,37 @@ "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" -#: fntsample.c:331 +#: src/fntsample.c:333 #, c-format msgid "Unicode blocks file should be given at most once!\n" msgstr "" -#: fntsample.c:337 +#: src/fntsample.c:339 #, c-format msgid "Failed to load any blocks from the blocks file!\n" msgstr "" -#: fntsample.c:343 fntsample.c:361 +#: src/fntsample.c:345 src/fntsample.c:363 #, c-format msgid "Font file name should be given only once!\n" msgstr "" -#: fntsample.c:350 +#: src/fntsample.c:352 #, c-format msgid "Output file name should be given only once!\n" msgstr "" -#: fntsample.c:378 -#, c-format -msgid "Cairo >= 1.15.4 is required for this option!\n" -msgstr "" - -#: fntsample.c:408 -#, c-format -msgid "Pango >= 1.37 is required for this option!\n" -msgstr "" - -#: fntsample.c:424 +#: src/fntsample.c:419 #, c-format msgid "Font index should be non-negative!\n" msgstr "" -#: fntsample.c:428 +#: src/fntsample.c:424 #, c-format msgid "-s and -g cannot be used together!\n" msgstr "" -#: fntsample.c:825 +#: src/fntsample.c:782 #, c-format msgid "" "Usage: %s [ OPTIONS ] -f FONT-FILE -o OUTPUT-FILE\n" @@ -64,7 +54,7 @@ "\n" msgstr "" -#: fntsample.c:827 +#: src/fntsample.c:784 #, c-format msgid "" "Options:\n" @@ -87,67 +77,75 @@ "output)\n" " --no-embed, -e Don't embed the font in the output " "file, draw the glyphs instead\n" -" --use-pango -p Use Pango for drawing glyph cells\n" " --include-range, -i RANGE Show characters in RANGE\n" " --exclude-range, -x RANGE Do not show characters in RANGE\n" " --style, -t \"STYLE: VAL\" Set STYLE to value VAL\n" msgstr "" -#: fntsample.c:844 +#: src/fntsample.c:801 #, c-format msgid "" "\n" "Supported styles (and default values):\n" msgstr "" -#: fntsample.c:972 +#: src/fntsample.c:864 #, c-format msgid "Not enough space for rendering glyphs. Make cell font smaller.\n" msgstr "" -#: fntsample.c:977 +#: src/fntsample.c:870 #, c-format msgid "The font has strange metrics: ascent + descent = %g\n" msgstr "" +#: src/fntsample.c:903 +#, c-format +msgid "Failed to parse environment variable SOURCE_DATE_EPOCH.\n" +msgstr "" + #. TRANSLATORS: 'freetype' is a name of a library, and should be left untranslated -#: fntsample.c:1016 +#: src/fntsample.c:929 #, c-format msgid "%s: freetype error\n" msgstr "" -#: fntsample.c:1022 +#: src/fntsample.c:937 #, c-format msgid "%s: failed to open font file %s\n" msgstr "" -#: fntsample.c:1031 +#: src/fntsample.c:947 #, c-format msgid "%s: failed to create new font face\n" msgstr "" #. TRANSLATORS: 'cairo' is a name of a library, and should be left untranslated -#: fntsample.c:1046 +#: src/fntsample.c:966 #, c-format msgid "%s: failed to create cairo surface: %s\n" msgstr "" -#: fntsample.c:1054 +#: src/fntsample.c:974 #, c-format msgid "%s: cairo_create failed: %s\n" msgstr "" -#: fntsample.c:1066 -#, c-format -msgid "%s: failed to create scaled font: %s\n" -msgstr "" - -#: pdfoutline.pl:41 +#: scripts/pdfoutline.pl:42 #, perl-format msgid "Usage: %s input.pdf outline.txt out.pdf\n" msgstr "" -#: pdfoutline.pl:102 +#: scripts/pdfoutline.pl:141 scripts/pdf-extract-outline.pl:174 #, perl-brace-format msgid "Cannot open outline file '{outlinefile}'" msgstr "" + +#: scripts/pdf-extract-outline.pl:48 +#, perl-format +msgid "Usage: %s input.pdf outline.txt\n" +msgstr "" + +#: scripts/pdf-extract-outline.pl:181 +msgid "Extracting outlines from encrypted files is not supported" +msgstr "" diff -Nru fntsample-5.3/po/uk.po fntsample-5.4/po/uk.po --- fntsample-5.3/po/uk.po 2018-12-26 07:13:37.000000000 +0000 +++ fntsample-5.4/po/uk.po 2021-08-01 16:18:33.000000000 +0000 @@ -6,59 +6,49 @@ msgstr "" "Project-Id-Version: fntsample\n" "Report-Msgid-Bugs-To: eugen@debian.org\n" -"POT-Creation-Date: 2017-10-22 01:32+0200\n" -"PO-Revision-Date: 2017-10-25 17:33+0100\n" -"Last-Translator: Eugeniy Meshcheryakov \n" +"POT-Creation-Date: 2021-08-01 16:57+0200\n" +"PO-Revision-Date: 2021-08-01 16:59+0200\n" +"Last-Translator: Ievgenii Meshcheriakov \n" "Language-Team: Ukrainian <>\n" "Language: uk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Lokalize 2.0\n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<" -"=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" +"X-Generator: Poedit 3.0\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" -#: fntsample.c:331 +#: src/fntsample.c:333 #, c-format msgid "Unicode blocks file should be given at most once!\n" msgstr "Файл блоків Unicode можна вказувати тільки один раз!\n" -#: fntsample.c:337 +#: src/fntsample.c:339 #, c-format msgid "Failed to load any blocks from the blocks file!\n" msgstr "Не вдалося завантажити жодного блока з файлу блоків!\n" -#: fntsample.c:343 fntsample.c:361 +#: src/fntsample.c:345 src/fntsample.c:363 #, c-format msgid "Font file name should be given only once!\n" msgstr "Назву файлу шрифтів потрібно вказувати тільки один раз!\n" -#: fntsample.c:350 +#: src/fntsample.c:352 #, c-format msgid "Output file name should be given only once!\n" msgstr "Назву файлу результату потрібно вказувати тільки один раз!\n" -#: fntsample.c:378 -#, c-format -msgid "Cairo >= 1.15.4 is required for this option!\n" -msgstr "Для цієї опції потрібно Cairo >= 1.15.4!\n" - -#: fntsample.c:408 -#, c-format -msgid "Pango >= 1.37 is required for this option!\n" -msgstr "Для цієї опції потрібно Pango >= 1.37!\n" - -#: fntsample.c:424 +#: src/fntsample.c:419 #, c-format msgid "Font index should be non-negative!\n" msgstr "Індекс шрифту повинен бути ненегативним!\n" -#: fntsample.c:428 +#: src/fntsample.c:424 #, c-format msgid "-s and -g cannot be used together!\n" msgstr "Не можна одночасно використовувати -s та -g!\n" -#: fntsample.c:825 +#: src/fntsample.c:782 #, c-format msgid "" "Usage: %s [ OPTIONS ] -f FONT-FILE -o OUTPUT-FILE\n" @@ -69,7 +59,7 @@ " %s -h\n" "\n" -#: fntsample.c:827 +#: src/fntsample.c:784 #, c-format msgid "" "Options:\n" @@ -92,14 +82,13 @@ "output)\n" " --no-embed, -e Don't embed the font in the output " "file, draw the glyphs instead\n" -" --use-pango -p Use Pango for drawing glyph cells\n" " --include-range, -i RANGE Show characters in RANGE\n" " --exclude-range, -x RANGE Do not show characters in RANGE\n" " --style, -t \"STYLE: VAL\" Set STYLE to value VAL\n" msgstr "" "Опції:\n" -" --blocks-file, -b ФАЙЛ-БЛОКІВ Читати інформацію про блоки Unicode" -" з ФАЙЛУ-БЛОКІВ\n" +" --blocks-file, -b ФАЙЛ-БЛОКІВ Читати інформацію про блоки " +"Unicode з ФАЙЛУ-БЛОКІВ\n" " --font-file, -f ФАЙЛ-ШРИФТУ Створити зразки ФАЙЛУ-ШРИФТУ\n" " --font-index, -n ІНД Індекс шрифту в ФАЙЛІ-ШРИФТУ\n" " --output-file, -o ФАЙЛ-РЕЗУЛЬТАТУ Зберегти зразки в ФАЙЛ-РЕЗУЛЬТАТУ\n" @@ -117,13 +106,11 @@ "для формату PDF)\n" " --no-embed, -e Не вбудовувати шрифти, натомість " "малювати символи\n" -" --use-pango -p Використовувати Pango для " -"креслення символів\n" " --include-range, -i ПРОМІЖОК Показати символи з ПРОМІЖКУ\n" " --exclude-range, -x ПРОМІЖОК Не показувати символи з ПРОМІЖКУ\n" " --style, -t \"СТИЛЬ: ЗНАЧ\" Встановити ЗНАЧЕННЯ СТИЛЮ\n" -#: fntsample.c:844 +#: src/fntsample.c:801 #, c-format msgid "" "\n" @@ -132,57 +119,76 @@ "\n" "Підтримувані стилі (та значення за замовчанням):\n" -#: fntsample.c:972 +#: src/fntsample.c:864 #, c-format msgid "Not enough space for rendering glyphs. Make cell font smaller.\n" msgstr "" "Недостатньо місця для відображення символів. Зробіть шрифт комірок меншим.\n" -#: fntsample.c:977 +#: src/fntsample.c:870 #, c-format msgid "The font has strange metrics: ascent + descent = %g\n" msgstr "Шрифт має дивні метрики: ascent + descent = %g\n" +#: src/fntsample.c:903 +#, c-format +msgid "Failed to parse environment variable SOURCE_DATE_EPOCH.\n" +msgstr "Не вдалося розібрати змінну середовища SOURCE_DATE_EPOCH.\n" + #. TRANSLATORS: 'freetype' is a name of a library, and should be left untranslated -#: fntsample.c:1016 +#: src/fntsample.c:929 #, c-format msgid "%s: freetype error\n" msgstr "%s: помилка freetype\n" -#: fntsample.c:1022 +#: src/fntsample.c:937 #, c-format msgid "%s: failed to open font file %s\n" msgstr "%s: неможливо відкрити файл шрифту %s\n" -#: fntsample.c:1031 +#: src/fntsample.c:947 #, c-format msgid "%s: failed to create new font face\n" msgstr "%s: неможливо створити новий шрифт\n" #. TRANSLATORS: 'cairo' is a name of a library, and should be left untranslated -#: fntsample.c:1046 +#: src/fntsample.c:966 #, c-format msgid "%s: failed to create cairo surface: %s\n" msgstr "%s: неможливо створити нову поверхню cairo: %s\n" -#: fntsample.c:1054 +#: src/fntsample.c:974 #, c-format msgid "%s: cairo_create failed: %s\n" msgstr "%s: помилка при виконанні cairo_create: %s\n" -#: fntsample.c:1066 -#, c-format -msgid "%s: failed to create scaled font: %s\n" -msgstr "%s: неможливо створити масштабований шрифт: %s\n" - -#: pdfoutline.pl:41 +#: scripts/pdfoutline.pl:42 #, perl-format msgid "Usage: %s input.pdf outline.txt out.pdf\n" msgstr "Використання: %s оригінальний.pdf зміст.txt результат.pdf\n" -#: pdfoutline.pl:102 +#: scripts/pdfoutline.pl:141 scripts/pdf-extract-outline.pl:174 #, perl-brace-format msgid "Cannot open outline file '{outlinefile}'" msgstr "Неможливо відкрити файл змісту '{outlinefile}'" +#: scripts/pdf-extract-outline.pl:48 +#, perl-format +msgid "Usage: %s input.pdf outline.txt\n" +msgstr "Використання: %s вхідний.pdf зміст.txt\n" + +#: scripts/pdf-extract-outline.pl:181 +msgid "Extracting outlines from encrypted files is not supported" +msgstr "Видобування змісту зашифрованих файлів не підтримується" + +#, c-format +#~ msgid "Cairo >= 1.15.4 is required for this option!\n" +#~ msgstr "Для цієї опції потрібно Cairo >= 1.15.4!\n" +#, c-format +#~ msgid "%s: failed to create scaled font: %s\n" +#~ msgstr "%s: неможливо створити масштабований шрифт: %s\n" + +#, c-format +#~ msgid "Pango >= 1.37 is required for this option!\n" +#~ msgstr "Для цієї опції потрібно Pango >= 1.37!\n" diff -Nru fntsample-5.3/read_blocks.c fntsample-5.4/read_blocks.c --- fntsample-5.3/read_blocks.c 2018-12-26 07:13:37.000000000 +0000 +++ fntsample-5.4/read_blocks.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,65 +0,0 @@ -#include "unicode_blocks.h" -#include -#include -#include - -struct unicode_block *read_blocks(const char *file_name, int *n) -{ - int nalloc = 256; - struct unicode_block *blocks = calloc(nalloc, sizeof(struct unicode_block)); - *n = 0; - - FILE *input_file = fopen(file_name, "r"); - if (!input_file) { - perror("fopen"); - exit(7); - } - - char *line = NULL; - size_t len = 0; - ssize_t nread; - - while ((nread = getline(&line, &len, input_file)) != -1) { - unsigned long block_start, block_end; - char block_name[256]; - - if (nread >= (ssize_t)sizeof(block_name)) - continue; - - int matched = sscanf(line, "%lx..%lx; %[^\r\n]", &block_start, &block_end, block_name); - if (matched == 3) { - struct unicode_block *b = blocks + *n; - b->start = block_start; - b->end = block_end; - b->name = strdup(block_name); - if (b->name == NULL) { - perror("strdup"); - exit(8); - } - - *n += 1; - if (*n >= nalloc) { - int new_nalloc = nalloc + 256; - struct unicode_block *new_blocks = realloc(blocks, - new_nalloc * sizeof(struct unicode_block)); - if (new_blocks == NULL) { - perror("realloc"); - exit(9); - } - memset(new_blocks + nalloc, 0, (new_nalloc - nalloc) * sizeof(struct unicode_block)); - nalloc = new_nalloc; - blocks = new_blocks; - } - } - } - free(line); - - if (*n == 0) { - free(blocks); - return NULL; - } else if (*n < nalloc) { - blocks = realloc(blocks, *n * sizeof(struct unicode_block)); - } - - return blocks; -} diff -Nru fntsample-5.3/README.rst fntsample-5.4/README.rst --- fntsample-5.3/README.rst 2018-12-26 07:13:37.000000000 +0000 +++ fntsample-5.4/README.rst 2021-08-01 16:18:33.000000000 +0000 @@ -3,8 +3,8 @@ |build| |license| -.. |build| image:: https://travis-ci.org/eugmes/fntsample.svg?branch=master - :target: https://travis-ci.org/eugmes/fntsample +.. |build| image:: https://github.com/eugmes/fntsample/actions/workflows/ci.yml/badge.svg + :target: https://github.com/eugmes/fntsample/actions/workflows/ci.yml .. |license| image:: https://img.shields.io/badge/License-GPL%20v3-blue.svg :alt: License: GPL v3 :target: https://www.gnu.org/licenses/gpl-3.0 @@ -48,7 +48,8 @@ `fontconfig `_, `FreeType2 `_, `GLib `_, -`Pango `_. +`Pango `_, +`gettext `_. They should be available in most Linux distributions. Additionally Unicode `blocks `_ file is required. @@ -63,6 +64,18 @@ The last step will install files under ``/usr/local`` by default. This can be overridden by adding ``-DCMAKE_INSTALL_PREFIX=/another/prefix`` to the ``cmake`` invocation. +``fntsample`` could be built using `Homebrew `_ on macOS. +Use the following commands to install dependencies and configure the build +environment before building the code:: + + % brew install cmake pgk-config gettext cairo pango fontconfig freetype glib + % export CMAKE_PREFIX_PATH=/usr/local/opt/gettext + +Alternatively you can install ``fntsample`` using a Homebrew +`formula `_:: + + % brew install eugmes/fntsample/fntsample + Usage ----- diff -Nru fntsample-5.3/scripts/CMakeLists.txt fntsample-5.4/scripts/CMakeLists.txt --- fntsample-5.3/scripts/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ fntsample-5.4/scripts/CMakeLists.txt 2021-08-01 16:18:33.000000000 +0000 @@ -0,0 +1,20 @@ +configure_file(pdf-extract-outline.1.in pdf-extract-outline.1 @ONLY) +configure_file(pdfoutline.1.in pdfoutline.1 @ONLY) +configure_file(pdfoutline.pl pdfoutline ESCAPE_QUOTES @ONLY) +configure_file(pdf-extract-outline.pl pdf-extract-outline ESCAPE_QUOTES @ONLY) + +add_translatable_sources(pdfoutline.pl pdf-extract-outline.pl) + +install( + PROGRAMS + "${CMAKE_CURRENT_BINARY_DIR}/pdfoutline" + "${CMAKE_CURRENT_BINARY_DIR}/pdf-extract-outline" + DESTINATION ${CMAKE_INSTALL_BINDIR} +) + +install( + FILES + "${CMAKE_CURRENT_BINARY_DIR}/pdfoutline.1" + "${CMAKE_CURRENT_BINARY_DIR}/pdf-extract-outline.1" + DESTINATION "${CMAKE_INSTALL_MANDIR}/man1" +) diff -Nru fntsample-5.3/scripts/pdf-extract-outline.1.in fntsample-5.4/scripts/pdf-extract-outline.1.in --- fntsample-5.3/scripts/pdf-extract-outline.1.in 1970-01-01 00:00:00.000000000 +0000 +++ fntsample-5.4/scripts/pdf-extract-outline.1.in 2021-08-01 16:18:33.000000000 +0000 @@ -0,0 +1,22 @@ +.\" -*- nroff -*- +.TH pdf-extract-outline 1 "@DATE@" "@CMAKE_PROJECT_VERSION@" "@CMAKE_PROJECT_NAME@" +.SH NAME +pdf-extract-outline \- extract outlines (aka bookmarks) from PDF files +.SH SYNOPSIS +.B pdf-extract-outline +.I input.pdf outlines.txt +.SH DESCRIPTION +\fBpdf-extract-outline\fP reads input file given as first argument and extracts outlines +into the file given as second argument. +The output format is compatible with one accepted by \fBpdfoutline\fB. +.SH OPTIONS +.B pdf-extract-outline +accepts no options. +.SH SEE ALSO +.B pdfoutline(1) +.SH AUTHOR +.B pdf-extract-outline +author is Ievgenii Meshcheriakov +.br +\fBpdf-extract-outline\fP is part of \fBfntsample\fP and can be downoaded from +<@CMAKE_PROJECT_HOMEPAGE_URL@>. diff -Nru fntsample-5.3/scripts/pdf-extract-outline.pl fntsample-5.4/scripts/pdf-extract-outline.pl --- fntsample-5.3/scripts/pdf-extract-outline.pl 1970-01-01 00:00:00.000000000 +0000 +++ fntsample-5.4/scripts/pdf-extract-outline.pl 2021-08-01 16:18:33.000000000 +0000 @@ -0,0 +1,188 @@ +#! /usr/bin/env perl +# This file is in the public domain +# Author: Ievgenii Meshcheriakov +# +# This program extracts outlines from PDF files. The outlines +# are stored into a text file that could be used with pdfoutline +# program. +# +# Usage: pdf-extract-outline input.pdf outline.txt + +use strict; +use warnings; +use feature qw(say); +use PDF::API2; +use Locale::TextDomain('@CMAKE_PROJECT_NAME@', '@CMAKE_INSTALL_FULL_LOCALEDIR@'); +use POSIX qw(:locale_h); +use Encode qw(decode find_encoding); +use Encode::Guess; +use List::MoreUtils qw(first_index); + +my $fallback_encoding = 'PDFDocumentEncoding'; + +eval { + require Encode::PDFDocumentEncoding; + Encode::Guess->set_suspects(qw/PDFDocumentEncoding/); + 1; +} or do { + warn 'Encode::PDFDocumentEncoding is missing, falling back to ASCII'; + $fallback_encoding = 'ascii'; +}; + +sub decode_pdf { + my ($s) = @_; + + eval { + decode('Guess', $s); + } or do { + decode $fallback_encoding, $s, sub { + my $code = shift; + my $repr = sprintf "\\x%02X", $code; + warn "$fallback_encoding \"$repr\" does not map to Unicode"; + return q{?}; + }; + } +} + +sub usage { + printf __"Usage: %s input.pdf outline.txt\n", $0; +} + +sub search_tree { + my ($tree, $key) = @_; + + if ($tree->{'Limits'}) { + my ($first, $last) = @{$tree->{'Limits'}->val}; + return if (($key lt $first->val) or ($key gt $last->val)); + } + + if ($tree->{'Names'}) { + my @arr = @{$tree->{'Names'}->val}; + for (my $i = 0; $i < $#arr; $i += 2) { + return $arr[$i + 1] if ($arr[$i]->val eq $key); + } + } + + if ($tree->{'Kids'}) { + foreach my $kid (@{$tree->{'Kids'}->val}) { + my $result = search_tree($kid->val, $key); + return $result if $result; + } + } + + return; +} + +sub extract_outlines { + my ($pdf, $level, $outline, $F) = @_; + + OUTLINE: for (; $outline; $outline = $outline->{'Next'}) { + $outline = $outline->val; + + my $raw_title = $outline->{'Title'}->val; + my $title = decode_pdf($raw_title); + my $dest; + + if ($outline->{'Dest'}) { + $dest = $outline->{'Dest'}; + } elsif ($outline->{'A'}) { + my $a = $outline->{'A'}->val; + # TODO Search for GoTo entry + if ($a->{'S'}->val eq 'GoTo') { + $dest = $a->{'D'}; + } else { + warn 'Action is not GoTo'; + next OUTLINE; + } + } else { + warn "No Dest or A entry for '$title'"; + next OUTLINE; + } + + if (ref($dest) eq 'PDF::API2::Basic::PDF::Name') { + # Find the destination in Dest dictionary in Root object. + my $named_ref = $dest->val; + my $dests = $pdf->{'pdf'}->{'Root'}->{'Dests'}->val; + $dest = $dests->{$named_ref}; + } elsif (ref($dest) eq 'PDF::API2::Basic::PDF::String') { + # Find the destination in Dest tree in Names dictionary of Root object + my $names = $pdf->{'pdf'}->{'Root'}->{'Names'}->val; + my $tree = $names->{'Dests'}->val; + my $name = $dest->val; + $dest = search_tree($tree, $name); + unless ($dest) { + warn "No Dest found with name '$name'"; + next OUTLINE; + } + } + + if (ref($dest) eq 'PDF::API2::Basic::PDF::Objind') { + $dest = $dest->val; + } + + if (ref($dest) eq 'PDF::API2::Basic::PDF::Dict') { + $dest = $dest->{'D'}; + } + + if (ref($dest) eq 'PDF::API2::Basic::PDF::Array') { + $dest = $dest->val; + } + + unless ($dest) { + warn "Destination not found for '$title'"; + next OUTLINE; + } + + my $page = $dest->[0]; + my $page_no; + + if (ref($page) eq 'PDF::API2::Basic::PDF::Number') { + # Some documents use numbers even for pages in the current document + $page_no = $page->val + 1; + } else { + my $page_idx = first_index { $_ == $page } @{$pdf->{'pagestack'}}; + + if ($page_idx == -1) { + warn "Page not found in the page stack for '$title'"; + next OUTLINE; + } + + $page_no = $page_idx + 1; + } + + print {$F} "$level $page_no $title\n"; + + my $sub_outlines = $outline->{'First'}; + if ($sub_outlines) { + extract_outlines($pdf, $level + 1, $sub_outlines, $F); + } + } +} + +setlocale(LC_ALL, q{}); + +if ($#ARGV != 1) { + usage; + exit 1; +} + +my ($pdffile, $outlinefile) = @ARGV; + +my $pdf = PDF::API2->open($pdffile); + +open my $outline_fh, '>:encoding(UTF-8)', $outlinefile + or die __x("Cannot open outline file '{outlinefile}'", + outlinefile => $outlinefile); + +my $outlines = $pdf->{'pdf'}->{'Root'}->{'Outlines'}; + +if ($outlines) { + if ($pdf->{'pdf'}->{'Encrypt'}) { + die __('Extracting outlines from encrypted files is not supported'); + } + + my $first = $outlines->val->{'First'}; + extract_outlines($pdf, 0, $first, $outline_fh); +} + +close $outline_fh; diff -Nru fntsample-5.3/scripts/pdfoutline.1.in fntsample-5.4/scripts/pdfoutline.1.in --- fntsample-5.3/scripts/pdfoutline.1.in 1970-01-01 00:00:00.000000000 +0000 +++ fntsample-5.4/scripts/pdfoutline.1.in 2021-08-01 16:18:33.000000000 +0000 @@ -0,0 +1,74 @@ +.\" -*- nroff -*- +.TH pdfoutline 1 "@DATE@" "@CMAKE_PROJECT_VERSION@" "@CMAKE_PROJECT_NAME@" +.SH NAME +pdfoutline \- add outlines (aka bookmarks) to PDF files +\" macros +.de SAMPLE +.br +.RS +.nf +.nh +.. +.de ESAMPLE +.hy +.fi +.RE +.. +.SH SYNOPSIS +.B pdfoutline +.I input.pdf outlines.txt output.pdf +.SH DESCRIPTION +\fBpdfoutline\fP reads input file given as first argument, adds outlines from text file given +as second argument, and saves result to file with name given as third argument. +.P +File with outlines information should consist of lines in the following format: +.SAMPLE +\fI\fP \fI\fP Outline text +.ESAMPLE +.P +\fI\fP and \fI\fP should be integers. +Each field should be separated by exactly one space symbol. +All values for \fI\fP should be greater or equal than that of the first line. +Page numeration starts with 1. +.P +Outlines file can contain comments that start with # in first column. +Comments and empty lines are ignored. +The text is expected to be in UTF-8 encoding. +.SH OPTIONS +.B pdfoutline +accepts no options. +.SH EXAMPLES +Here is example of outlines data file: +.SAMPLE +0 1 Document title +1 1 Chapter 1 +2 1 Chapter 1.1 +2 2 Chapter 1.2 +1 3 Chapter 2 +.ESAMPLE +.P +Using this file will result in outlines like the following: +.SAMPLE +Document title ++-Chapter 1 +| +-Chapter 1.1 +| +-Chapter 1.2 ++-Chapter 2 +.ESAMPLE +.SH BUGS +Due to a bug in Perl library \fBPDF::API2 v2.039\fP and earlier, some +Unicode characters are handled incorrectly and cause outline string +corruptions. For example, everything after the CJK character U+4E0A (上) +will get corrupted in the PDF output because its UTF-16 encoding contains +byte 0x0A, which happens to be an ASCII newline character. +.P +For user convenience, this \fBpdfoutline\fP version includes a workaround +that should allow flawless operation down to \fBPDF::API2 v2.034\fP. Users +of even earlier versions should upgrade \fBPDF::API2\fP. +.P +.SH AUTHOR +.B pdfoutline +author is Ievgenii Meshcheriakov +.br +\fBpdfoutline\fP is part of \fBfntsample\fP and can be downoaded from +<@CMAKE_PROJECT_HOMEPAGE_URL@>. diff -Nru fntsample-5.3/scripts/pdfoutline.pl fntsample-5.4/scripts/pdfoutline.pl --- fntsample-5.3/scripts/pdfoutline.pl 1970-01-01 00:00:00.000000000 +0000 +++ fntsample-5.4/scripts/pdfoutline.pl 2021-08-01 16:18:33.000000000 +0000 @@ -0,0 +1,154 @@ +#! /usr/bin/env perl +# This file is in public domain +# Author: Ievgenii Meshcheriakov +# +# This program adds outlines to pdf files. +# Usage: pdfoutline input.pdf outline.txt out.pdf +# +# File given as second argument should contain outline information in +# form: +# +# Some text +# +# where and are integers. Values for should be greater +# or equal than that of first line. Page numeration starts with 1. +# +# Outlines file can contain comments that start with # in first column. Comments +# and empty lines are ignored. +# +# Example file: +# 0 1 Document title +# 1 1 Chapter 1 +# 2 1 Chapter 1.1 +# 2 2 Chapter 1.2 +# 1 3 Chapter 2 +# +# This file will result in outline like the following: +# +# Document title +# +-Chapter 1 +# | +-Chapter 1.1 +# | +-Chapter 1.2 +# +-Chapter 2 + +use strict; +use warnings; +use PDF::API2; +use Locale::TextDomain('@CMAKE_PROJECT_NAME@', '@CMAKE_INSTALL_FULL_LOCALEDIR@'); +use POSIX qw(:locale_h); +use Encode qw(encode); + +sub usage { + printf __"Usage: %s input.pdf outline.txt out.pdf\n", $0; +} + +# get first non-empty non-comment line +sub get_line { + my ($F) = @_; + my $line; + + while ($line = <$F>) { + chomp $line; + # skip comments ... + next if $line =~ /^#/; + # ... and empty lines + next if $line eq q{}; + last; + } + return $line; +} + +# Encode string to UTF-16BE with BOM if it contains non-ASCII characters +sub encode_pdf_text { + my ($str) = @_; + + if ($str !~ /[^[:ascii:]]/) { + return $str; + } else { + if (PDF::API2->VERSION ge "2.034") { + # Perl PDF::API2 >= 2.034 already handles non-ASCII characters + # automatically. This also avoids a bug before v2.040. + # See: https://rt.cpan.org/Public/Bug/Display.html?id=33497 + return $str; + } else { + # Buggy before PDF::API2 v2.040. + # See: https://rt.cpan.org/Public/Bug/Display.html?id=134957 + return encode('UTF-16', $str); + } + } +} + +sub add_outlines { + my ($pdf, $parent, $line, $F) = @_; + my $cur_outline; + + my ($level) = split / /, $line; + + MAINLOOP: while ($line) { + my ($new_level, $page, $text) = split / /, $line, 3; + + if ($new_level > $level) { + $line = add_outlines($pdf, $cur_outline, $line, $F); + next MAINLOOP; + } elsif ($new_level < $level) { + return $line; + } else { + $cur_outline = $parent->outline; + $cur_outline->title(encode_pdf_text($text)); + # FIXME it should be posible to make it easier + my $pdfpage = $pdf->{pagestack}->[$page - 1]; + $cur_outline->dest($pdfpage); + } + + $line = get_line($F); + } +} + +# Create new outlines object ignorig outlines that can be +# already present in the PDF file. +sub new_outlines { + my ($pdf) = @_; + + require PDF::API2::Outlines; + $pdf->{'pdf'}->{'Root'}->{'Outlines'} = PDF::API2::Outlines->new($pdf); + my $obj = $pdf->{'pdf'}->{'Root'}->{'Outlines'}; + + $pdf->{'pdf'}->new_obj($obj) unless $obj->is_obj($pdf->{'pdf'}); + $pdf->{'pdf'}->out_obj($obj); + $pdf->{'pdf'}->out_obj($pdf->{'pdf'}->{'Root'}); + + return $obj; +} + +setlocale(LC_ALL, q{}); + +if ($#ARGV != 2) { + usage; + exit 1; +} + +if (PDF::API2->VERSION le "2.033") { + print STDERR "Warning: Perl PDF::API2 v2.033 or earlier detected.\n"; + print STDERR "It's known to have an outline corruption bug!\n"; + print STDERR "See pdfoutline man page for more information.\n"; +} + +my ($inputfile, $outlinefile, $outputfile) = @ARGV; + +my $pdf = PDF::API2->open($inputfile); + +open my $outline_fh, '<:encoding(UTF-8)', $outlinefile + or die __x("Cannot open outline file '{outlinefile}'", + outlinefile => $outlinefile); + +my $line = get_line($outline_fh); + +# create new outlines here, don't try to use old ones +my $outlines = new_outlines($pdf); + +add_outlines($pdf, $outlines, $line, $outline_fh) if $line; +close $outline_fh; + +$pdf->saveas($outputfile); + +exit 0; diff -Nru fntsample-5.3/src/CMakeLists.txt fntsample-5.4/src/CMakeLists.txt --- fntsample-5.3/src/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ fntsample-5.4/src/CMakeLists.txt 2021-08-01 16:18:33.000000000 +0000 @@ -0,0 +1,55 @@ +configure_file(config.h.in config.h ESCAPE_QUOTES @ONLY) +configure_file(fntsample.1.in fntsample.1 @ONLY) + +set( + C_WARNING_FLAGS + -Wcast-align -Werror-implicit-function-declaration -Wchar-subscripts + -Wall -W -Wpointer-arith -Wwrite-strings -Wformat-security + -Wmissing-format-attribute -fno-common -Wundef + CACHE STRING "Warning flags for C compiler" +) + +add_executable(gen-unicode-blocks EXCLUDE_FROM_ALL + gen_unicode_blocks.c + read_blocks.c +) + +target_compile_features(gen-unicode-blocks PRIVATE c_std_99) +target_compile_options(gen-unicode-blocks PRIVATE ${C_WARNING_FLAGS}) + +add_custom_command( + OUTPUT static_unicode_blocks.c + COMMAND "$" "${UNICODE_BLOCKS}" static_unicode_blocks.c + MAIN_DEPENDENCY ${UNICODE_BLOCKS} + DEPENDS gen-unicode-blocks + VERBATIM +) + +add_executable(fntsample + fntsample.c + read_blocks.c + ${CMAKE_CURRENT_BINARY_DIR}/static_unicode_blocks.c +) + +add_translatable_sources(fntsample.c read_blocks.c) + +target_compile_features(fntsample PRIVATE c_std_99) + +target_include_directories(fntsample PRIVATE + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} +) + +target_link_libraries(fntsample PRIVATE + m + Intl::Intl + PkgConfig::pkgs +) + +target_compile_options(fntsample PRIVATE ${C_WARNING_FLAGS}) + +# TODO use improved install handling in CMake 3.14 +install(TARGETS fntsample DESTINATION ${CMAKE_INSTALL_BINDIR}) + +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/fntsample.1" + DESTINATION "${CMAKE_INSTALL_MANDIR}/man1") diff -Nru fntsample-5.3/src/config.h.in fntsample-5.4/src/config.h.in --- fntsample-5.3/src/config.h.in 1970-01-01 00:00:00.000000000 +0000 +++ fntsample-5.4/src/config.h.in 2021-08-01 16:18:33.000000000 +0000 @@ -0,0 +1,7 @@ +#ifndef CONFIG_H +#define CONFIG_H + +#cmakedefine CMAKE_PROJECT_NAME "@CMAKE_PROJECT_NAME@" +#cmakedefine CMAKE_INSTALL_FULL_LOCALEDIR "@CMAKE_INSTALL_FULL_LOCALEDIR@" + +#endif diff -Nru fntsample-5.3/src/fntsample.1.in fntsample-5.4/src/fntsample.1.in --- fntsample-5.3/src/fntsample.1.in 1970-01-01 00:00:00.000000000 +0000 +++ fntsample-5.4/src/fntsample.1.in 2021-08-01 16:18:33.000000000 +0000 @@ -0,0 +1,152 @@ +.\" -*- nroff -*- +.TH fntsample 1 "@DATE@" "@CMAKE_PROJECT_VERSION@" "@CMAKE_PROJECT_NAME@" +.SH NAME +fntsample \- PDF and PostScript font samples generator +\" macros +.de SAMPLE +.br +.RS +.nf +.nh +.. +.de ESAMPLE +.hy +.fi +.RE +.. +.SH SYNOPSIS +.B fntsample +.BI "[ " OPTIONS " ]" +.BI "\-f " FONT-FILE " \-o " OUTPUT-FILE +.br +.B fntsample \-h +.SH DESCRIPTION +.B fntsample +program can be used to generate font samples that show Unicode coverage +of the font and are similar in appearance to Unicode charts. Samples can be saved +into PDF (default) or PostScript file. +.SH OPTIONS +.B fntsample +supports the following options. +.TP +.BI "\-\-blocks\-file, \-b " BLOCKS-FILE +Read Unicode blocks information from +.IR BLOCKS-FILE . +.TP +.BI "\-\-font\-file, \-f " FONT-FILE +Make samples of +.IR FONT-FILE . +.TP +.BI "\-\-font\-index, \-n " IDX +Font index for \fIFONT-FILE\fP specified using \fB\-\-font\-file\fP option. +Useful for files that contain multiple fonts, like TrueType Collections (.ttc). +By default font with index 0 is used. +.TP +.BI "\-\-output\-file, \-o " OUTPUT-FILE +Write output to +.IR OUTPUT-FILE . +.TP +.BI "\-\-other\-font\-file, \-d " OTHER-FONT +Compare +.I FONT-FILE +with +.IR OTHER-FONT . +Glyphs added to +.I FONT-FILE +will be highlighted. +.TP +.BI "\-\-other\-index, \-m " IDX +Font index for \fIOTHER-FONT\fP specified using \fB\-\-other\-font\-file\fP option. +.TP +.BI "\-\-postscript\-output, \-s" +Use PostScript format for output instead of PDF. +.TP +.BI "\-\-svg, \-g" +Use SVG format for output. +The generated document contains one page. +Use range selection options to specify which. +.TP +.BI "\-\-print\-outline, \-l" +Print document outlines data to standard output. +This data can be used to add outlines (aka bookmarks) to resulting PDF file with \fBpdfoutline\fP program. +.TP +.BI "\-\-write\-outline, \-w" +Write document outlines directly (only in PDF output). +.TP +.BI "\-\-include\-range, \-i " RANGE +Show characters in \fIRANGE\fP. +.TP +.BI "\-\-exclude\-range, \-x " RANGE +Do not show characters in \fIRANGE\fP. +.TP +.BI "\-\-style, \-t \(dq" STYLE ": " VAL "\(dq" +Set \fISTYLE\fP to value \fIVAL\fP. +Run \fBfntsample\fP with option \fB\-\-help\fP to see list of styles and default values. +.TP +.BI "\-\-no\-embed, \-e" +Draw the outlines of the glyphs instead of embedding them in the PDF file. +This can be used when embedding the font is not desired or not allowed. +.TP +.BI "\-\-help, \-h" +Display help text and exit. +.P +Parameter \fIRANGE\fP for \fB\-i\fP and \fB\-x\fP can be given as one integer or a pair of +integers delimited by minus sign (\-). +Integers can be specified in decimal, hexadecimal (0x...) or octal (0...) format. +One integer of a pair can be missing (\-N can be used to specify all characters with codes less +or equal to N, and N\- for all characters with codes greather or equal to N). +Multiple \fB\-i\fP and \fB\-x\fP options can be used. +.SH COLORS +Glyph cells can have one of several background colors. +Meaning of those colors is following: +.TP +.B white +normal glyph present in the font, this includes space glyphs that are usually invisible; +.TP +.B gray +this glyph is defined in Unicode but not present in the font; +.TP +.B blue +this is a control character; +.TP +.B black +this glyph is not defined in Unicode; +.TP +.B yellow +this is a new glyph (only when used with \fB\-d\fP). +.SH ENVIRONMENT +.TP +.B SOURCE_DATE_EPOCH +If $\fBSOURCE_DATE_EPOCH\fP is set, its value is interpreted as Unix timestamp to be used +for creation date of generated PDF files. +This is useful for making builds that use \fBfntsample\fP reproducible. +.SH EXAMPLES +.RI "Make PDF samples for " font.ttf " and write them to file " samples.pdf : +.SAMPLE +fntsample \-f font.ttf \-o samples.pdf +.ESAMPLE +.PP +.RI "Make PDF samples for " font.ttf ", compare it with " oldfont.ttf +.RI "and highlight new glyphs. Write output to file " samples.pdf : +.SAMPLE +fntsample \-f font.ttf \-d oldfont.ttf \-o samples.pdf +.ESAMPLE +.PP +.RI "Make PostScript samples for " font.ttf " and write output to file " samples.ps . +Show only glyphs for characters with codes less or equal to U+04FF but exclude U+0370\-U+03FF: +.SAMPLE +fntsample \-f font.ttf \-s \-o samples.ps \-i \-0x04FF \-x 0x0370\-0x03FF +.ESAMPLE +.PP +.RI "Make PDF samples for " font.ttf " and save output to file " samples.pdf " adding outlines to it:" +.SAMPLE +fntsample \-f font.ttf \-o temp.pdf \-l > outlines.txt +pdfoutline temp.pdf outlines.txt samples.pdf +.ESAMPLE +.SH AUTHOR +Copyright \(co Ievgenii Meshcheriakov +.br +Homepage: <@CMAKE_PROJECT_HOMEPAGE_URL@> +.SH SEE ALSO +.PP +.BR pdfoutline (1) diff -Nru fntsample-5.3/src/fntsample.c fntsample-5.4/src/fntsample.c --- fntsample-5.3/src/fntsample.c 1970-01-01 00:00:00.000000000 +0000 +++ fntsample-5.4/src/fntsample.c 2021-08-01 16:18:33.000000000 +0000 @@ -0,0 +1,990 @@ +/* Copyright © Євгеній Мещеряков + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include +#include +#include FT_FREETYPE_H +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "unicode_blocks.h" +#include "static_unicode_blocks.h" +#include "config.h" + +#define _(str) gettext(str) + +#define POINTS_PER_INCH 72 + +#define A4_WIDTH (8.3 * POINTS_PER_INCH) +#define A4_HEIGHT (11.7 * POINTS_PER_INCH) + +#define xmin_border (POINTS_PER_INCH / 1.5) +#define ymin_border POINTS_PER_INCH +#define cell_width ((A4_WIDTH - 2 * xmin_border) / 16) +#define cell_height ((A4_HEIGHT - 2 * ymin_border) / 16) + +static double cell_x(double x_min, int pos) +{ + return x_min + cell_width * (pos / 16); +} + +static double cell_y(int pos) +{ + return ymin_border + cell_height * (pos % 16); +} + +static struct option longopts[] = { + {"blocks-file", 1, 0, 'b'}, + {"font-file", 1, 0, 'f'}, + {"output-file", 1, 0, 'o'}, + {"help", 0, 0, 'h'}, + {"other-font-file", 1, 0, 'd'}, + {"postscript-output", 0, 0, 's'}, + {"svg", 0, 0, 'g'}, + {"print-outline", 0, 0, 'l'}, + {"write-outline", 0, 0, 'w'}, + {"include-range", 1, 0, 'i'}, + {"exclude-range", 1, 0, 'x'}, + {"style", 1, 0, 't'}, + {"font-index", 1, 0, 'n'}, + {"other-index", 1, 0, 'm'}, + {"no-embed", 0, 0, 'e'}, + {"use-pango", 0, 0, 'p'}, /* For compatibility with version <= 5.3 */ + {0, 0, 0, 0} +}; + +struct range { + uint32_t first; + uint32_t last; + bool include; + struct range *next; +}; + +static const char *font_file_name; +static const char *other_font_file_name; +static const char *output_file_name; +static bool postscript_output; +static bool svg_output; +static bool print_outline; +static bool write_outline; +static bool no_embed; +static struct range *ranges; +static struct range *last_range; +static int font_index; +static int other_index; + +struct fntsample_style { + const char *const name; + const char *const default_val; + char *val; +}; + +static struct fntsample_style styles[] = { + { "header-font", "Sans Bold 12", NULL }, + { "font-name-font", "Serif Bold 12", NULL }, + { "table-numbers-font", "Sans 10", NULL }, + { "cell-numbers-font", "Mono 8", NULL }, + { NULL, NULL, NULL } +}; + +struct table_fonts { + PangoFontDescription *header; + PangoFontDescription *font_name; + PangoFontDescription *table_numbers; + PangoFontDescription *cell_numbers; +}; + +static struct table_fonts table_fonts; + +static double cell_label_offset; +static double cell_glyph_bot_offset; +static double glyph_baseline_offset; +static double font_scale; + +static const struct unicode_block *unicode_blocks; + +static void usage(const char *); + +static struct fntsample_style *find_style(const char *name) +{ + for (struct fntsample_style *style = styles; style->name; style++) { + if (!strcmp(name, style->name)) { + return style; + } + } + + return NULL; +} + +static int set_style(const char *name, const char *val) +{ + struct fntsample_style *style = find_style(name); + + if (!style) { + return -1; + } + + char *new_val = strdup(val); + if (!new_val) { + return -1; + } + + if (style->val) { + free(style->val); + } + + style->val = new_val; + + return 0; +} + +static const char *get_style(const char *name) +{ + struct fntsample_style *style = find_style(name); + + if (!style) { + return NULL; + } + + return style->val ? style->val : style->default_val; +} + +static int parse_style_string(char *s) +{ + char *n = strchr(s, ':'); + if (!n) { + return -1; + } + + *n++ = '\0'; + return set_style(s, n); +} + +/* + * Update output range. + * + * Returns -1 on error. + */ +static int add_range(char *range, bool include) +{ + uint32_t first = 0, last = 0xffffffff; + char *endptr; + + char *minus = strchr(range, '-'); + + if (minus) { + if (minus != range) { + *minus = '\0'; + first = strtoul(range, &endptr, 0); + if (*endptr) { + return -1; + } + } + + if (*(minus + 1)) { + last = strtoul(minus + 1, &endptr, 0); + if (*endptr) { + return -1; + } + } else if (minus == range) { + return -1; + } + } else { + first = strtoul(range, &endptr, 0); + if (*endptr) + return -1; + last = first; + } + + if (first > last) { + return -1; + } + + struct range *r = malloc(sizeof(*r)); + if (!r) { + return -1; + } + + r->first = first; + r->last = last; + r->include = include; + r->next = NULL; + + if (ranges) { + last_range->next = r; + } else { + ranges = r; + } + + last_range = r; + + return 0; +} + +/* + * Check if character with the given code belongs + * to output range specified by the user. + */ +static bool in_range(uint32_t c) +{ + bool in = ranges ? (!ranges->include) : 1; + + for (struct range *r = ranges; r; r = r->next) { + if ((c >= r->first) && (c <= r->last)) { + in = r->include; + } + } + return in; +} + +/* + * Get glyph index for the next glyph from the given font face, that + * represents character from output range specified by the user. + * + * Returns character code, updates 'idx'. + * 'idx' can became 0 if there are no more glyphs. + */ +static FT_ULong get_next_char(FT_Face face, FT_ULong charcode, FT_UInt *idx) +{ + FT_ULong rval = charcode; + + do { + rval = FT_Get_Next_Char(face, rval, idx); + } while (*idx && !in_range(rval)); + + return rval; +} + +/* + * Locate first character from the given font face that belongs + * to the user-specified output range. + * + * Returns character code, updates 'idx' with glyph index. + * Glyph index can became 0 if there are no matching glyphs in the font. + */ +static FT_ULong get_first_char(FT_Face face, FT_UInt *idx) +{ + FT_ULong rval = FT_Get_First_Char(face, idx); + + if (*idx && !in_range(rval)) { + rval = get_next_char(face, rval, idx); + } + + return rval; +} + +/* + * Create Pango layout for the given text. + * Updates 'r' with text extents. + * Returned layout should be freed using g_object_unref(). + */ +static PangoLayout *layout_text(cairo_t *cr, PangoFontDescription *ftdesc, const char *text, PangoRectangle *r) +{ + PangoLayout *layout = pango_cairo_create_layout(cr); + pango_layout_set_font_description(layout, ftdesc); + pango_layout_set_text(layout, text, -1); + pango_layout_get_extents(layout, r, NULL); + + return layout; +} + +static void parse_options(int argc, char * const argv[]) +{ + for (;;) { + int n; + int c = getopt_long(argc, argv, "b:f:o:hd:sglwi:x:t:n:m:ep", longopts, NULL); + + if (c == -1) { + break; + } + + switch (c) { + case 'b': + if (unicode_blocks) { + fprintf(stderr, _("Unicode blocks file should be given at most once!\n")); + exit(1); + } + + unicode_blocks = read_blocks(optarg, &n); + if (n == 0) { + fprintf(stderr, _("Failed to load any blocks from the blocks file!\n")); + exit(6); + } + break; + case 'f': + if (font_file_name) { + fprintf(stderr, _("Font file name should be given only once!\n")); + exit(1); + } + font_file_name = optarg; + break; + case 'o': + if (output_file_name) { + fprintf(stderr, _("Output file name should be given only once!\n")); + exit(1); + } + output_file_name = optarg; + break; + case 'h': + usage(argv[0]); + exit(0); + break; + case 'd': + if (other_font_file_name) { + fprintf(stderr, _("Font file name should be given only once!\n")); + exit(1); + } + other_font_file_name = optarg; + break; + case 's': + postscript_output = true; + break; + case 'g': + svg_output = true; + break; + case 'l': + print_outline = true; + break; + case 'w': + write_outline = true; + break; + case 'i': + case 'x': + if (add_range(optarg, c == 'i')) { + usage(argv[0]); + exit(1); + } + break; + case 't': + if (parse_style_string(optarg) == -1) { + usage(argv[0]); + exit(1); + } + break; + case 'n': + font_index = atoi(optarg); + break; + case 'm': + other_index = atoi(optarg); + break; + case 'e': + no_embed = true; + break; + case 'p': + /* Ignored for compatibility */ + break; + case '?': + default: + usage(argv[0]); + exit(1); + break; + } + } + + if (!font_file_name || !output_file_name) { + usage(argv[0]); + exit(1); + } + + if (font_index < 0 || other_index < 0) { + fprintf(stderr, _("Font index should be non-negative!\n")); + exit(1); + } + + if (postscript_output && svg_output) { + fprintf(stderr, _("-s and -g cannot be used together!\n")); + exit(1); + } + + if (!unicode_blocks) { + unicode_blocks = static_unicode_blocks; + } +} + +/* + * Locate unicode block that contains given character code. + * Returns this block or NULL if not found. + */ +static const struct unicode_block *get_unicode_block(unsigned long charcode) +{ + for (const struct unicode_block *block = unicode_blocks; block->name; block++) { + if ((charcode >= block->start) && (charcode <= block->end)) { + return block; + } + } + + return NULL; +} + +/* + * Check if the given character code belongs to the given Unicode block. + */ +static bool is_in_block(unsigned long charcode, const struct unicode_block *block) +{ + return ((charcode >= block->start) && (charcode <= block->end)); +} + +/* + * Format and print/write outline information, if requested by the user. + */ +static void outline(cairo_surface_t *surface, int level, int page, const char *text) +{ + if (print_outline) { + printf("%d %d %s\n", level, page, text); + } + + if (write_outline && cairo_surface_get_type(surface) == CAIRO_SURFACE_TYPE_PDF) { + int len = snprintf(0, 0, "page=%d", page); + char *dest = malloc(len + 1); + sprintf(dest, "page=%d", page); + + /* FIXME passing level here is not correct. */ + cairo_pdf_surface_add_outline(surface, level, text, dest, CAIRO_PDF_OUTLINE_FLAG_OPEN); + free(dest); + } +} + +/* + * Draw header of a page. + * Header shows font name and current Unicode block. + */ +static void draw_header(cairo_t *cr, const char *face_name, const char *block_name) +{ + PangoRectangle r; + + PangoLayout *layout = layout_text(cr, table_fonts.font_name, face_name, &r); + cairo_move_to(cr, (A4_WIDTH - pango_units_to_double(r.width))/2.0, 30.0); + pango_cairo_show_layout_line(cr, pango_layout_get_line_readonly(layout, 0)); + g_object_unref(layout); + + layout = layout_text(cr, table_fonts.header, block_name, &r); + cairo_move_to(cr, (A4_WIDTH - pango_units_to_double(r.width))/2.0, 50.0); + pango_cairo_show_layout_line(cr, pango_layout_get_line_readonly(layout, 0)); + g_object_unref(layout); +} + +/* + * Highlight the cell with given coordinates. + * Used to highlight new glyphs. + */ +static void highlight_cell(cairo_t *cr, double x, double y) +{ + cairo_save(cr); + cairo_set_source_rgb(cr, 1.0, 1.0, 0.6); + cairo_rectangle(cr, x, y, cell_width, cell_height); + cairo_fill(cr); + cairo_restore(cr); +} + +/* + * Draw table grid with row and column numbers. + */ +static void draw_grid(cairo_t *cr, unsigned int x_cells, + unsigned long block_start) +{ + const double x_min = (A4_WIDTH - x_cells * cell_width) / 2; + const double x_max = (A4_WIDTH + x_cells * cell_width) / 2; + const double table_height = A4_HEIGHT - ymin_border * 2; + + cairo_set_line_width(cr, 1.0); + cairo_rectangle(cr, x_min, ymin_border, x_max - x_min, table_height); + cairo_move_to(cr, x_min, ymin_border); + cairo_line_to(cr, x_min, ymin_border - 15.0); + cairo_move_to(cr, x_max, ymin_border); + cairo_line_to(cr, x_max, ymin_border - 15.0); + cairo_stroke(cr); + + cairo_set_line_width(cr, 0.5); + /* draw horizontal lines */ + for (int i = 1; i < 16; i++) { + // TODO: use better name instead of just POINTS_PER_INCH + cairo_move_to(cr, x_min, POINTS_PER_INCH + i * table_height/16); + cairo_line_to(cr, x_max, POINTS_PER_INCH + i * table_height/16); + } + + /* draw vertical lines */ + for (unsigned int i = 1; i < x_cells; i++) { + cairo_move_to(cr, x_min + i * cell_width, ymin_border); + cairo_line_to(cr, x_min + i * cell_width, A4_HEIGHT - ymin_border); + } + cairo_stroke(cr); + + /* draw glyph numbers */ + char buf[17]; + buf[1] = '\0'; +#define hexdigs "0123456789ABCDEF" + + for (int i = 0; i < 16; i++) { + buf[0] = hexdigs[i]; + + PangoRectangle r; + PangoLayout *layout = layout_text(cr, table_fonts.table_numbers, buf, &r); + cairo_move_to(cr, + x_min - pango_units_to_double(PANGO_RBEARING(r)) - 5.0, + POINTS_PER_INCH + (i+0.5) * table_height/16 + pango_units_to_double(PANGO_DESCENT(r))/2); + pango_cairo_show_layout_line(cr, pango_layout_get_line_readonly(layout, 0)); + cairo_move_to(cr, x_min + x_cells * cell_width + 5.0, + POINTS_PER_INCH + (i+0.5) * table_height/16 + pango_units_to_double(PANGO_DESCENT(r))/2); + pango_cairo_show_layout_line(cr, pango_layout_get_line_readonly(layout, 0)); + g_object_unref(layout); + } + + for (unsigned int i = 0; i < x_cells; i++) { + snprintf(buf, sizeof(buf), "%03lX", block_start / 16 + i); + + PangoRectangle r; + PangoLayout *layout = layout_text(cr, table_fonts.table_numbers, buf, &r); + cairo_move_to(cr, + x_min + i*cell_width + (cell_width - pango_units_to_double(r.width))/2, + ymin_border - 5.0); + pango_cairo_show_layout_line(cr, pango_layout_get_line_readonly(layout, 0)); + g_object_unref(layout); + } +} + +/* + * Fill empty cell. Color of the fill depends on the character properties. + */ +static void fill_empty_cell(cairo_t *cr, double x, double y, unsigned long charcode) +{ + cairo_save(cr); + if (g_unichar_isdefined(charcode)) { + if (g_unichar_iscntrl(charcode)) + cairo_set_source_rgb(cr, 0.0, 0.0, 0.5); + else + cairo_set_source_rgb(cr, 0.5, 0.5, 0.5); + } + cairo_rectangle(cr, x, y, cell_width, cell_height); + cairo_fill(cr); + cairo_restore(cr); +} + +/* + * Draw label with character code. + */ +static void draw_charcode(cairo_t *cr, double x, double y, FT_ULong charcode) +{ + char buf[9]; + snprintf(buf, sizeof(buf), "%04lX", charcode); + + PangoRectangle r; + PangoLayout *layout = layout_text(cr, table_fonts.cell_numbers, buf, &r); + cairo_move_to(cr, x + (cell_width - pango_units_to_double(r.width))/2.0, + y + cell_height - cell_label_offset); + pango_cairo_show_layout_line(cr, pango_layout_get_line_readonly(layout, 0)); + g_object_unref(layout); +} + +/* + * Draws tables for all characters in the given Unicode block. + * Use font described by face and ft_face. Start from character + * with given charcode (it should belong to the given Unicode + * block). After return 'charcode' equals the last character code + * of the block. + * + * Returns number of pages drawn. + */ +static int draw_unicode_block(cairo_t *cr, PangoLayout *layout, + FT_Face ft_face, const char *font_name, unsigned long *charcode, + const struct unicode_block *block, FT_Face ft_other_face) +{ + unsigned long prev_charcode; + unsigned long prev_cell; + int npages = 0; + FT_UInt idx = FT_Get_Char_Index(ft_face, *charcode); + + do { + unsigned long offset = ((*charcode - block->start) / 0x100) * 0x100; + unsigned long tbl_start = block->start + offset; + unsigned long tbl_end = tbl_start + 0xFF > block->end ? + block->end + 1 : tbl_start + 0x100; + unsigned int rows = (tbl_end - tbl_start) / 16; + double x_min = (A4_WIDTH - rows * cell_width) / 2; + bool filled_cells[256]; /* 16x16 glyphs max */ + + cairo_save(cr); + draw_header(cr, font_name, block->name); + prev_cell = tbl_start - 1; + + memset(filled_cells, '\0', sizeof(filled_cells)); + + /* + * Fill empty cells and calculate coordinates of the glyphs. + * Also highlight cells if needed. + */ + do { + /* the current glyph position in the table */ + int charpos = *charcode - tbl_start; + + /* fill empty cells before the current glyph */ + for (unsigned long i = prev_cell + 1; i < *charcode; i++) { + int pos = i - tbl_start; + fill_empty_cell(cr, cell_x(x_min, pos), cell_y(pos), i); + } + + /* if it is new glyph - highlight the cell */ + if (ft_other_face && !FT_Get_Char_Index(ft_other_face, *charcode)) { + highlight_cell(cr, cell_x(x_min, charpos), cell_y(charpos)); + } + + /* draw the character */ + char buf[9]; + gint len = g_unichar_to_utf8((gunichar)*charcode, buf); + pango_layout_set_text(layout, buf, len); + + double baseline = pango_units_to_double(pango_layout_get_baseline(layout)); + cairo_move_to(cr, cell_x(x_min, charpos), cell_y(charpos) + glyph_baseline_offset - baseline); + + if (no_embed) { + pango_cairo_layout_path(cr, layout); + } else { + pango_cairo_show_layout(cr, layout); + } + + filled_cells[charpos] = true; + + prev_charcode = *charcode; + prev_cell = *charcode; + *charcode = get_next_char(ft_face, *charcode, &idx); + } while (idx && (*charcode < tbl_end) && is_in_block(*charcode, block)); + + /* Fill remaining empty cells */ + for (unsigned long i = prev_cell + 1; i < tbl_end; i++) { + int pos = i - tbl_start; + fill_empty_cell(cr, cell_x(x_min, pos), cell_y(pos), i); + } + + /* + * Charcodes are drawn here to avoid switching between the charcode + * font and the cell font for each filled cell. + */ + for (unsigned long i = 0; i < tbl_end - tbl_start; i++) { + if (filled_cells[i]) { + draw_charcode(cr, cell_x(x_min, i), cell_y(i), + i + tbl_start); + } + } + + draw_grid(cr, rows, tbl_start); + npages++; + cairo_show_page(cr); + cairo_restore(cr); + } while (idx && is_in_block(*charcode, block)); + + *charcode = prev_charcode; + return npages; +} + +static PangoLayout *create_glyph_layout(cairo_t *cr, FcConfig *fc_config, FcPattern *fc_font) +{ + PangoFontMap *fontmap = pango_cairo_font_map_new_for_font_type(CAIRO_FONT_TYPE_FT); + pango_fc_font_map_set_config(PANGO_FC_FONT_MAP(fontmap), fc_config); + PangoContext *context = pango_font_map_create_context(fontmap); + pango_cairo_update_context(cr, context); + + PangoFontDescription *font_desc = pango_fc_font_description_from_pattern(fc_font, FALSE); + PangoLayout *layout = pango_layout_new(context); + pango_layout_set_font_description(layout, font_desc); + pango_layout_set_width(layout, pango_units_from_double(cell_width)); + pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER); + + g_object_unref(context); + g_object_unref(fontmap); + pango_font_description_free(font_desc); + + return layout; +} + +/* + * The main drawing function. + */ +static void draw_glyphs(cairo_t *cr, FT_Face ft_face, + FT_Face ft_other_face) +{ + FcConfig *fc_config = FcConfigCreate(); + FcConfigAppFontAddFile(fc_config, (const FcChar8 *)font_file_name); + + FcPattern *fc_pat = FcPatternCreate(); + FcPatternAddInteger(fc_pat, FC_INDEX, font_index); + + FcFontSet *fc_fontset = FcFontList(fc_config, fc_pat, NULL); + assert(fc_fontset->nfont > 0); + FcPattern *fc_font = fc_fontset->fonts[0]; + + const char *font_name; + if (FcPatternGetString(fc_font, FC_FULLNAME, 0, (FcChar8 **)&font_name) != FcResultMatch) { + font_name = "Unknown"; + } + + cairo_surface_t *surface = cairo_get_target(cr); + + int pageno = 1; + outline(surface, 0, pageno, font_name); + + PangoLayout *layout = create_glyph_layout(cr, fc_config, fc_font); + + FT_UInt idx; + FT_ULong charcode = get_first_char(ft_face, &idx); + + while (idx) { + const struct unicode_block *block = get_unicode_block(charcode); + if (block) { + outline(surface, 1, pageno, block->name); + int npages = draw_unicode_block(cr, layout, ft_face, font_name, + &charcode, block, ft_other_face); + pageno += npages; + } + + charcode = get_next_char(ft_face, charcode, &idx); + } + + g_object_unref(layout); + + FcPatternDestroy(fc_pat); + FcFontSetDestroy(fc_fontset); + FcConfigDestroy(fc_config); +} + +/* + * Print usage instructions and default values for styles + */ +static void usage(const char *cmd) +{ + fprintf(stderr, _("Usage: %s [ OPTIONS ] -f FONT-FILE -o OUTPUT-FILE\n" + " %s -h\n\n") , cmd, cmd); + fprintf(stderr, _("Options:\n" + " --blocks-file, -b BLOCKS-FILE Read Unicode blocks information from BLOCKS-FILE\n" + " --font-file, -f FONT-FILE Create samples of FONT-FILE\n" + " --font-index, -n IDX Font index in FONT-FILE\n" + " --output-file, -o OUTPUT-FILE Save samples to OUTPUT-FILE\n" + " --help, -h Show this information message and exit\n" + " --other-font-file, -d OTHER-FONT Compare FONT-FILE with OTHER-FONT and highlight added glyphs\n" + " --other-index, -m IDX Font index in OTHER-FONT\n" + " --postscript-output, -s Use PostScript format for output instead of PDF\n" + " --svg, -g Use SVG format for output\n" + " --print-outline, -l Print document outlines data to standard output\n" + " --write-outline, -w Write document outlines (only in PDF output)\n" + " --no-embed, -e Don't embed the font in the output file, draw the glyphs instead\n" + " --include-range, -i RANGE Show characters in RANGE\n" + " --exclude-range, -x RANGE Do not show characters in RANGE\n" + " --style, -t \"STYLE: VAL\" Set STYLE to value VAL\n")); + + fprintf(stderr, _("\nSupported styles (and default values):\n")); + + for (const struct fntsample_style *style = styles; style->name; style++) { + fprintf(stderr, "\t%s (%s)\n", style->name, style->default_val); + } +} + +/* + * Initialize fonts used to print table heders and character codes. + */ +static void init_table_fonts(void) +{ + /* FIXME is this correct? */ + PangoCairoFontMap *map = (PangoCairoFontMap *)pango_cairo_font_map_get_default(); + + pango_cairo_font_map_set_resolution(map, POINTS_PER_INCH); + + table_fonts.header = pango_font_description_from_string(get_style("header-font")); + table_fonts.font_name = pango_font_description_from_string(get_style("font-name-font")); + table_fonts.table_numbers = pango_font_description_from_string(get_style("table-numbers-font")); + table_fonts.cell_numbers = pango_font_description_from_string(get_style("cell-numbers-font")); +} + +/* + * Calculate various offsets. + */ +static void calculate_offsets(cairo_t *cr) +{ + PangoRectangle extents; + /* Assume that vertical extents does not depend on actual text */ + PangoLayout *l = layout_text(cr, table_fonts.cell_numbers, "0123456789ABCDEF", &extents); + g_object_unref(l); + /* Unsolved mistery of pango's font metrics.... */ + double digits_ascent = pango_units_to_double(PANGO_DESCENT(extents)); + double digits_descent = -pango_units_to_double(PANGO_ASCENT(extents)); + + cell_label_offset = digits_descent + 2; + cell_glyph_bot_offset = cell_label_offset + digits_ascent + 2; +} + +/* + * Calculate font scaling + */ +void calc_font_scaling(FT_Face ft_face) +{ + cairo_font_face_t *cr_face = cairo_ft_font_face_create_for_ft_face(ft_face, 0); + cairo_font_options_t *options = cairo_font_options_create(); + + /* First create font with size 1 and measure it */ + cairo_matrix_t font_matrix; + cairo_matrix_init_identity(&font_matrix); + cairo_matrix_t ctm; + cairo_matrix_init_identity(&ctm); + + /* Turn off rounding, so we can get real metrics */ + cairo_font_options_set_hint_metrics(options, CAIRO_HINT_METRICS_OFF); + cairo_scaled_font_t *cr_font = cairo_scaled_font_create(cr_face, &font_matrix, &ctm, options); + cairo_font_extents_t extents; + cairo_scaled_font_extents(cr_font, &extents); + + /* Use some magic to find the best font size... */ + double tgt_size = cell_height - cell_glyph_bot_offset - 2; + if (tgt_size <= 0) { + fprintf(stderr, _("Not enough space for rendering glyphs. Make cell font smaller.\n")); + exit(5); + } + + double act_size = extents.ascent + extents.descent; + if (act_size <= 0) { + fprintf(stderr, _("The font has strange metrics: ascent + descent = %g\n"), act_size); + exit(5); + } + + font_scale = tgt_size / act_size; + if (font_scale > 1) + font_scale = trunc(font_scale); // just to make numbers nicer + if (font_scale > 20) + font_scale = 20; // Do not make font larger than in previous versions + + cairo_scaled_font_destroy(cr_font); + + /* Create the font once again, but this time scaled */ + cairo_matrix_init_scale(&font_matrix, font_scale, font_scale); + cr_font = cairo_scaled_font_create(cr_face, &font_matrix, &ctm, options); + cairo_scaled_font_extents(cr_font, &extents); + glyph_baseline_offset = (tgt_size - (extents.ascent + extents.descent)) / 2 + 2 + extents.ascent; + cairo_scaled_font_destroy(cr_font); +} + +/* + * Configure DPF surface metadata so fntsample can be used with + * repeatable builds. + */ +static void set_repeatable_pdf_metadata(cairo_surface_t *surface) +{ + char *source_date_epoch = getenv("SOURCE_DATE_EPOCH"); + + if (source_date_epoch) { + char *endptr; + time_t now = strtoul(source_date_epoch, &endptr, 10); + + if (*endptr != 0) { + fprintf(stderr, _("Failed to parse environment variable SOURCE_DATE_EPOCH.\n")); + exit(1); + } + struct tm *build_time = gmtime(&now); + char buffer[25]; + strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%S%z", build_time); + + cairo_pdf_surface_set_metadata(surface, + CAIRO_PDF_METADATA_CREATE_DATE, + buffer); + } +} + +int main(int argc, char **argv) +{ + setlocale(LC_ALL, ""); + bindtextdomain(CMAKE_PROJECT_NAME, CMAKE_INSTALL_FULL_LOCALEDIR); + textdomain(CMAKE_PROJECT_NAME); + + parse_options(argc, argv); + + FT_Library library; + FT_Error error = FT_Init_FreeType(&library); + + if (error) { + /* TRANSLATORS: 'freetype' is a name of a library, and should be left untranslated */ + fprintf(stderr, _("%s: freetype error\n"), argv[0]); + exit(3); + } + + FT_Face face; + error = FT_New_Face(library, font_file_name, font_index, &face); + + if (error) { + fprintf(stderr, _("%s: failed to open font file %s\n"), argv[0], font_file_name); + exit(4); + } + + FT_Face other_face = NULL; + + if (other_font_file_name) { + error = FT_New_Face(library, other_font_file_name, other_index, &other_face); + + if (error) { + fprintf(stderr, _("%s: failed to create new font face\n"), argv[0]); + exit(4); + } + } + + cairo_surface_t *surface; + + if (postscript_output) { + surface = cairo_ps_surface_create(output_file_name, A4_WIDTH, A4_HEIGHT); + } else if (svg_output) { + surface = cairo_svg_surface_create(output_file_name, A4_WIDTH, A4_HEIGHT); + } else { + surface = cairo_pdf_surface_create(output_file_name, A4_WIDTH, A4_HEIGHT); /* A4 paper */ + set_repeatable_pdf_metadata(surface); + } + + cairo_status_t cr_status = cairo_surface_status(surface); + if (cr_status != CAIRO_STATUS_SUCCESS) { + /* TRANSLATORS: 'cairo' is a name of a library, and should be left untranslated */ + fprintf(stderr, _("%s: failed to create cairo surface: %s\n"), + argv[0], cairo_status_to_string(cr_status)); + exit(1); + } + + cairo_t *cr = cairo_create(surface); + cr_status = cairo_status(cr); + if (cr_status != CAIRO_STATUS_SUCCESS) { + fprintf(stderr, _("%s: cairo_create failed: %s\n"), + argv[0], cairo_status_to_string(cr_status)); + exit(1); + } + + cairo_surface_destroy(surface); + + init_table_fonts(); + calculate_offsets(cr); + + cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); + calc_font_scaling(face); + draw_glyphs(cr, face, other_face); + cairo_destroy(cr); + + return 0; +} diff -Nru fntsample-5.3/src/gen_unicode_blocks.c fntsample-5.4/src/gen_unicode_blocks.c --- fntsample-5.3/src/gen_unicode_blocks.c 1970-01-01 00:00:00.000000000 +0000 +++ fntsample-5.4/src/gen_unicode_blocks.c 2021-08-01 16:18:33.000000000 +0000 @@ -0,0 +1,77 @@ +/* Copyright © Євгеній Мещеряков + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "unicode_blocks.h" +#include + +static void write_header(FILE *f) +{ + fprintf(f, + "#include \"static_unicode_blocks.h\"\n" + "\n" + "const struct unicode_block static_unicode_blocks[] = {\n" + ); +} + +static void write_footer(FILE *f) +{ + fprintf(f, + " {0, 0, NULL},\n" + "};\n" + ); +} + +static void write_block(FILE *f, const struct unicode_block *block) +{ + fprintf(f, " {0x%04lx, 0x%04lx, \"%s\"},\n", + block->start, block->end, block->name); +} + +static void write_blocks(FILE *f, const struct unicode_block *blocks, int n) +{ + write_header(f); + for (int i = 0; i < n; i++) { + write_block(f, blocks + i); + } + write_footer(f); +} + +int main(int argc, char **argv) +{ + if (argc != 3) { + fprintf(stderr, "Usage: %s Blocks.txt output.c\n", argv[0]); + return 1; + } + + int n; + struct unicode_block *blocks = read_blocks(argv[1], &n); + + if (!blocks) { + fprintf(stderr, "Failed to read unicode blocks file.\n"); + return 2; + } + + FILE *f = fopen(argv[2], "wb"); + if (!f) { + perror("fopen"); + return 3; + } + + write_blocks(f, blocks, n); + free(blocks); + fclose(f); + + return 0; +} diff -Nru fntsample-5.3/src/read_blocks.c fntsample-5.4/src/read_blocks.c --- fntsample-5.3/src/read_blocks.c 1970-01-01 00:00:00.000000000 +0000 +++ fntsample-5.4/src/read_blocks.c 2021-08-01 16:18:33.000000000 +0000 @@ -0,0 +1,80 @@ +/* Copyright © Євгеній Мещеряков + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "unicode_blocks.h" +#include +#include +#include + +struct unicode_block *read_blocks(const char *file_name, int *n) +{ + int nalloc = 256; + struct unicode_block *blocks = calloc(nalloc, sizeof(struct unicode_block)); + *n = 0; + + FILE *input_file = fopen(file_name, "r"); + if (!input_file) { + perror("fopen"); + exit(7); + } + + char *line = NULL; + size_t len = 0; + ssize_t nread; + + while ((nread = getline(&line, &len, input_file)) != -1) { + unsigned long block_start, block_end; + char block_name[256]; + + if (nread >= (ssize_t)sizeof(block_name)) + continue; + + int matched = sscanf(line, "%lx..%lx; %[^\r\n]", &block_start, &block_end, block_name); + if (matched == 3) { + struct unicode_block *b = blocks + *n; + b->start = block_start; + b->end = block_end; + b->name = strdup(block_name); + if (b->name == NULL) { + perror("strdup"); + exit(8); + } + + *n += 1; + if (*n >= nalloc) { + int new_nalloc = nalloc + 256; + struct unicode_block *new_blocks = realloc(blocks, + new_nalloc * sizeof(struct unicode_block)); + if (new_blocks == NULL) { + perror("realloc"); + exit(9); + } + memset(new_blocks + nalloc, 0, (new_nalloc - nalloc) * sizeof(struct unicode_block)); + nalloc = new_nalloc; + blocks = new_blocks; + } + } + } + free(line); + + if (*n == 0) { + free(blocks); + return NULL; + } else if (*n < nalloc) { + blocks = realloc(blocks, *n * sizeof(struct unicode_block)); + } + + return blocks; +} diff -Nru fntsample-5.3/src/static_unicode_blocks.h fntsample-5.4/src/static_unicode_blocks.h --- fntsample-5.3/src/static_unicode_blocks.h 1970-01-01 00:00:00.000000000 +0000 +++ fntsample-5.4/src/static_unicode_blocks.h 2021-08-01 16:18:33.000000000 +0000 @@ -0,0 +1,11 @@ +/* + * This file is in public domain + * Author: Ievgenii Meshcheriakov + */ +#ifndef STATIC_UNICODE_BLOCKS_H +#define STATIC_UNICODE_BLOCKS_H +#include "unicode_blocks.h" + +extern const struct unicode_block static_unicode_blocks[]; + +#endif diff -Nru fntsample-5.3/src/unicode_blocks.h fntsample-5.4/src/unicode_blocks.h --- fntsample-5.3/src/unicode_blocks.h 1970-01-01 00:00:00.000000000 +0000 +++ fntsample-5.4/src/unicode_blocks.h 2021-08-01 16:18:33.000000000 +0000 @@ -0,0 +1,18 @@ +/* + * This file is in public domain + * Author: Ievgenii Meshcheriakov + */ +#ifndef UNICODE_BLOCKS_H +#define UNICODE_BLOCKS_H + +#include + +struct unicode_block { + unsigned long start; + unsigned long end; + const char *name; +}; + +struct unicode_block *read_blocks(const char *file_name, int *n); + +#endif diff -Nru fntsample-5.3/.travis.yml fntsample-5.4/.travis.yml --- fntsample-5.3/.travis.yml 2018-12-26 07:13:37.000000000 +0000 +++ fntsample-5.4/.travis.yml 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -sudo: false -dist: trusty -language: c - -compiler: -- gcc -- clang - -addons: - apt: - packages: - - gawk - - libcairo2-dev - - unicode-data - - libglib2.0-dev - - pkg-config - - libfreetype6-dev - - libpango1.0-dev - - gettext - - cmake - -script: -- mkdir build -- cd build -- cmake .. -DCMAKE_C_FLAGS=-Werror -- make VERBOSE=1 -- ./fntsample --help -- make DESTDIR=tmp install -- ls -lR tmp diff -Nru fntsample-5.3/unicode_blocks.h fntsample-5.4/unicode_blocks.h --- fntsample-5.3/unicode_blocks.h 2018-12-26 07:13:37.000000000 +0000 +++ fntsample-5.4/unicode_blocks.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -/* - * This file is in public domain - * Author: Eugeniy Meshcheryakov - */ -#ifndef UNICODE_BLOCKS_H -#define UNICODE_BLOCKS_H - -#include - -struct unicode_block { - unsigned long start; - unsigned long end; - const char *name; -}; - -extern const struct unicode_block static_unicode_blocks[]; - -struct unicode_block *read_blocks(const char *file_name, int *n); - -#endif