Commits (8)
  • Werner Lemberg's avatar
    Small clean-ups for the last few commits. · 205d1ae4
    Werner Lemberg authored
    * include/freetype/fttrace.h (afwarp): Removed.
    205d1ae4
  • Werner Lemberg's avatar
    [autofit] More clean-ups. · 825b7ea2
    Werner Lemberg authored
    * src/autofit/afhints.h (AF_GlyphHintsRec): Remove the no longer
    needed fields `xmin_delta` and `xmax_delta`.
    
    * src/autofit/afhints.c (af_glyph_hints_reload),
    src/autofit/afloader.c (af_loader_load_glyph): Updated.
    825b7ea2
  • Alexander Richardson's avatar
    * meson.build: Fix build for other UNIX systems (e.g., FreeBSD). · c5516e0f
    Alexander Richardson authored
    Without this change the build of `unix/ftsystem.c` fails because the
    `ftconfig.h` header that defines macros such as `HAVE_UNISTD_H` and
    `HAVE_FCNTL_H` is only being generated for Linux, macOS, and Cygwin
    systems:
    
    ```
    .../builds/unix/ftsystem.c:258:32: error:
        use of undeclared identifier 'O_RDONLY'
    file = open( filepathname, O_RDONLY );
    ```
    
    Instead of hardcoding a list of operating systems for this check,
    update the logic that decides whether to build the file and set a
    boolean flag that can be checked instead.
    c5516e0f
  • Anuj Verma's avatar
    [sdf] Improve documentation. · e592982a
    Anuj Verma authored
    e592982a
  • Oleg Oshmyan's avatar
    [base] Reject combinations of incompatible `FT_OPEN_XXX` flags. · a4c8f21a
    Oleg Oshmyan authored
    The three modes are mutually exclusive, and the documentation of the
    `FT_OPEN_XXX` constants notes this.  However, there was no check to
    validate this in the code, and the documentation on `FT_Open_Args`
    claimed that the corresponding bits were checked in a well-defined
    order, implying it was valid (if useless) to specify more than one.
    Ironically, this documented order did not agree with the actual
    code, so it could not be relied upon; hopefully, nobody did this and
    nobody will be hurt by the new validation.
    
    Even if multiple mode bits were allowed, they could cause memory
    leaks: if both `FT_OPEN_STREAM` and `stream` are set along with
    either `FT_OPEN_MEMORY` or `FT_OPEN_PATHNAME`, then `FT_Stream_New`
    allocated a new stream but `FT_Open_Face` marked it as an 'external'
    stream, so the stream object was never released.
    
    * src/base/ftobjs.c (FT_Stream_New): Reject incompatible
    `FT_OPEN_XXX` flags.
    a4c8f21a
  • Oleg Oshmyan's avatar
    [base] Fix `FT_Open_Face`'s handling of user-supplied streams. · 5d27b10f
    Oleg Oshmyan authored
    This was already true (though undocumented) most of the time, but
    not if `FT_NEW` inside `FT_Stream_New` failed or if the
    `FT_OPEN_XXX` flags were bad.
    
    Normally, `FT_Open_Face` calls `FT_Stream_New`, which returns the
    user-supplied stream unchanged, and in case of any subsequent error
    in `FT_Open_Face`, the stream is closed via `FT_Stream_Free`.
    
    Up to now, however, `FT_Stream_New` allocates a new stream even if
    it is already given one by the user.  If this allocation fails, the
    user-supplied stream is not returned to `FT_Open_Face` and never
    closed.  Moreover, the user cannot detect this situation: all they
    see is that `FT_Open_Face` returns `FT_Err_Out_Of_Memory`, but that
    can also happen after a different allocation fails within the main
    body of `FT_Open_Face`, when the user's stream has already been
    closed by `FT_Open_Face`.  It is plausible that the user stream's
    `close` method frees memory allocated for the stream object itself,
    so the user cannot defensively free it upon `FT_Open_Face` failure
    lest it ends up doubly freed.  All in all, this ends up leaking the
    memory/resources used by user's stream.
    
    Furthermore, `FT_Stream_New` simply returns an error if the
    `FT_OPEN_XXX` flags are unsupported, which can mean either an
    invalid combination of flags or a perfectly innocent
    `FT_OPEN_STREAM` on a FreeType build that lacks stream support.
    With this patch, the user-supplied stream is closed even in these
    cases, so the user can be sure that if `FT_Open_Face` failed, the
    stream is definitely closed.
    
    * src/base/ftobjs.c (FT_Stream_New): Don't allocate a buffer
    unnecessarily.
    Move error-handling code to make the control flow more obvious.
    Close user-supplied stream if the flags are unsupported.
    `FT_Stream_Open` always sets `pathname.pointer`, so remove the
    redundant (re)assignment.  None of the `FT_Stream_Open...` functions
    uses `stream->memory`, so keep just one assignment at the end,
    shared among all possible control flow paths.
    ('Unsupported flags' that may need a stream closure can be either an
    invalid combination of multiple `FT_OPEN_XXX` mode flags or a clean
    `FT_OPEN_STREAM` flag on a FreeType build that lacks stream
    support.)
    5d27b10f
  • Alexander Richardson's avatar
    Support architectures where `long` is smaller than pointers. · f7c6a06c
    Alexander Richardson authored
    I am currently trying to compile FreeType for CHERI-extended ISAs
    (CHERI-RISC-V and Arm's Morello), but I am getting compiler warnings
    from the `FT_UINT_TO_POINTER` macro.  When compiling with the CHERI
    Clang compiler, not using `uinptr_t` for casts between integers an
    pointers results in the following `-Werror` build failures:
    
    ```
    In file included from .../src/truetype/truetype.c:22:
      .../src/truetype/ttgload.c:1925:22: error:
        cast from provenance-free integer type to pointer type will
        give pointer that can not be dereferenced
        [-Werror,-Wcheri-capability-misuse]
      node->data = FT_UINT_TO_POINTER( glyph_index );
                   ^
      .../include/freetype/internal/compiler-macros.h:79:34: note:
        expanded from macro 'FT_UINT_TO_POINTER'
    ```
    
    * include/freetype/internal/compiler-macros.h (FT_UINT_TO_POINTER):
    The ISO C standard compliant fix for this would be to use
    `uintptr_t` from `stdint.h`, but I am not sure if this is supported
    by the minimum compiler version.  Therefore, use the
    compiler-defined `__UINTPTR_TYPE__` macro (supported in GCC 4.6+ and
    Clang since about 3.0) before checking for `_WIN64` and falling back
    to `unsigned long`.
    f7c6a06c
  • David Turner's avatar
    tests/scripts/download-test-fonts.sh rewrite in Python3 · 2811ef99
    David Turner authored
    Replaces download-test-fonts.sh with download-test-fonts.py
    which does the same work, and also avoids downloading anything
    if the files are already installed with the right content.
    
    Now uses the first 8 byte of each file's sha256 hash for the
    digest.
    2811ef99
2021-07-15 David Turner <david@freetype.org>
Replaces download-test-fonts.sh with download-test-fonts.py which
does the same work, and also avoids downloading anything if the
files are already installed with the right content.
Now uses the first 8 byte of each file's sha256 hash for the digest.
* tests/scripts/download-test-fonts.sh: Removed
* tests/scripts/download-test-fonts.py: New script
* tests/README.md: Updated
2021-07-15 Alex Richardson <Alexander.Richardson@cl.cam.ac.uk>
Support architectures where `long` is smaller than pointers.
I am currently trying to compile FreeType for CHERI-extended ISAs
(CHERI-RISC-V and Arm's Morello), but I am getting compiler warnings
from the `FT_UINT_TO_POINTER` macro. When compiling with the CHERI
Clang compiler, not using `uinptr_t` for casts between integers an
pointers results in the following `-Werror` build failures:
```
In file included from .../src/truetype/truetype.c:22:
.../src/truetype/ttgload.c:1925:22: error:
cast from provenance-free integer type to pointer type will
give pointer that can not be dereferenced
[-Werror,-Wcheri-capability-misuse]
node->data = FT_UINT_TO_POINTER( glyph_index );
^
.../include/freetype/internal/compiler-macros.h:79:34: note:
expanded from macro 'FT_UINT_TO_POINTER'
```
* include/freetype/internal/compiler-macros.h (FT_UINT_TO_POINTER):
The ISO C standard compliant fix for this would be to use
`uintptr_t` from `stdint.h`, but I am not sure if this is supported
by the minimum compiler version. Therefore, use the
compiler-defined `__UINTPTR_TYPE__` macro (supported in GCC 4.6+ and
Clang since about 3.0) before checking for `_WIN64` and falling back
to `unsigned long`.
2021-07-13 Oleg Oshmyan <chortos@inbox.lv>
[base] Fix `FT_Open_Face`'s handling of user-supplied streams.
This was already true (though undocumented) most of the time, but
not if `FT_NEW` inside `FT_Stream_New` failed or if the
`FT_OPEN_XXX` flags were bad.
Normally, `FT_Open_Face` calls `FT_Stream_New`, which returns the
user-supplied stream unchanged, and in case of any subsequent error
in `FT_Open_Face`, the stream is closed via `FT_Stream_Free`.
Up to now, however, `FT_Stream_New` allocates a new stream even if
it is already given one by the user. If this allocation fails, the
user-supplied stream is not returned to `FT_Open_Face` and never
closed. Moreover, the user cannot detect this situation: all they
see is that `FT_Open_Face` returns `FT_Err_Out_Of_Memory`, but that
can also happen after a different allocation fails within the main
body of `FT_Open_Face`, when the user's stream has already been
closed by `FT_Open_Face`. It is plausible that the user stream's
`close` method frees memory allocated for the stream object itself,
so the user cannot defensively free it upon `FT_Open_Face` failure
lest it ends up doubly freed. All in all, this ends up leaking the
memory/resources used by user's stream.
Furthermore, `FT_Stream_New` simply returns an error if the
`FT_OPEN_XXX` flags are unsupported, which can mean either an
invalid combination of flags or a perfectly innocent
`FT_OPEN_STREAM` on a FreeType build that lacks stream support.
With this patch, the user-supplied stream is closed even in these
cases, so the user can be sure that if `FT_Open_Face` failed, the
stream is definitely closed.
* src/base/ftobjs.c (FT_Stream_New): Don't allocate a buffer
unnecessarily.
Move error-handling code to make the control flow more obvious.
Close user-supplied stream if the flags are unsupported.
`FT_Stream_Open` always sets `pathname.pointer`, so remove the
redundant (re)assignment. None of the `FT_Stream_Open...` functions
uses `stream->memory`, so keep just one assignment at the end,
shared among all possible control flow paths.
('Unsupported flags' that may need a stream closure can be either an
invalid combination of multiple `FT_OPEN_XXX` mode flags or a clean
`FT_OPEN_STREAM` flag on a FreeType build that lacks stream
support.)
2021-07-13 Oleg Oshmyan <chortos@inbox.lv>
[base] Reject combinations of incompatible `FT_OPEN_XXX` flags.
The three modes are mutually exclusive, and the documentation of the
`FT_OPEN_XXX` constants notes this. However, there was no check to
validate this in the code, and the documentation on `FT_Open_Args`
claimed that the corresponding bits were checked in a well-defined
order, implying it was valid (if useless) to specify more than one.
Ironically, this documented order did not agree with the actual
code, so it could not be relied upon; hopefully, nobody did this and
nobody will be hurt by the new validation.
Even if multiple mode bits were allowed, they could cause memory
leaks: if both `FT_OPEN_STREAM` and `stream` are set along with
either `FT_OPEN_MEMORY` or `FT_OPEN_PATHNAME`, then `FT_Stream_New`
allocated a new stream but `FT_Open_Face` marked it as an 'external'
stream, so the stream object was never released.
* src/base/ftobjs.c (FT_Stream_New): Reject incompatible
`FT_OPEN_XXX` flags.
2021-07-12 Alex Richardson <Alexander.Richardson@cl.cam.ac.uk>
* meson.build: Fix build for other UNIX systems (e.g., FreeBSD).
Without this change the build of `unix/ftsystem.c` fails because the
`ftconfig.h` header that defines macros such as `HAVE_UNISTD_H` and
`HAVE_FCNTL_H` is only being generated for Linux, macOS, and Cygwin
systems:
```
.../builds/unix/ftsystem.c:258:32: error:
use of undeclared identifier 'O_RDONLY'
file = open( filepathname, O_RDONLY );
```
Instead of hardcoding a list of operating systems for this check,
update the logic that decides whether to build the file and set a
boolean flag that can be checked instead.
2021-07-12 Werner Lemberg <wl@gnu.org>
[autofit] More clean-ups.
* src/autofit/afhints.h (AF_GlyphHintsRec): Remove the no longer
needed fields `xmin_delta` and `xmax_delta`.
* src/autofit/afhints.c (af_glyph_hints_reload),
src/autofit/afloader.c (af_loader_load_glyph): Updated.
2021-07-12 Werner Lemberg <wl@gnu.org>
Small clean-ups for the last few commits.
* include/freetype/fttrace.h (afwarp): Removed.
2021-07-12 David Turner <david@freetype.org>
Remove obsolete AF_Angle type and related sources.
Move the af_sort_xxx() functions from afangles.c to afhints.c in
order to get rid of the obsolete angle-related types, macros and
function definitions.
* src/autofit/afangles.c: File removed. Functions related to
sorting moved to...
* src/autofit/afhints.c (af_sort_pos, af_sort_and_quantize_widths):
This file.
* src/autofit/afangles.h: File removed.
* src/autofit/aftypes.h: Updated.
* src/autofit/autofit.c: Updated.
* src/autofit/*: Remove code.
* src/autofit/rules.mk (AUTOF_DRV_SRC): Updated.
2021-07-12 David Turner <david@freetype.org>
Remove experimental auto-hinting 'warp' mode.
This feature was always experimental, and probably nevery worked
properly. This patch completely removes it from the source code,
This feature was always experimental, and probably never worked
properly. This patch completely removes it from the source code,
except for a documentation block describing it for historical
purpose.
purposes.
* devel/ftoption.h, include/freetype/config/ftoption.h: Remove
`AF_CONFIG_OPTION_USE_WARPER`.
* include/freetype/ftdriver.h: Document 'warping' property as
obsolete.
* devel/ftoption.h: Remove AF_CONFIG_OPTION_USE_WARPER.
* include/freetype/config/ftoption.h: Remove AF_CONFIG_OPTION_USE_WARPER.
* include/freetype/ftdriver.h: Document 'warping' property as obsolete.
* src/autofit/*: Remove any warp mode related code.
* src/autofit/afwarp.c, src/autofit/afwarp.h: Files removed.
* src/autofit/*: Remove any code related to warp mode.
2021-07-12 David Turner <david@freetype.org>
Remove experimental "Latin2" writing system (FT_OPTION_AUTOFIT2)
Remove experimental 'Latin2' writing system (`FT_OPTION_AUTOFIT2`).
This code has always been experimental and was never compiled anyway
(FT_OPTION_AUTOFIT2 does not appear in ftoption.h or even any of our
build files).
* include/freetype/internal/fttrace.h: Remove 'FT_TRACE_DEF( aflatin2 )'.
* src/autofit/aflatin2.[hc]: Removed.
* src/autofit/afloader.c: Remove undocumented hook to activate Latin2 system.
* src/autofit/afstyles.h: Remove ltn2_dflt style definition.
* src/autofit/afwrtsys.h: Remove LATIN2 writing system definition.
(`FT_OPTION_AUTOFIT2` does not appear in `ftoption.h` or even any of
our build files).
* include/freetype/internal/fttrace.h (aflatin2): Removed.
* src/autofit/aflatin2.h, src/autofit/aflatin2.c: Files removed.
* src/autofit/afloader.c: Remove undocumented hook to activate
Latin2 system.
* src/autofit/afstyles.h: Remove `ltn2_dflt` style definition.
* src/autofit/afwrtsys.h: Remove `LATIN2` writing system definition.
* src/autofit/autofit.c: Updated.
2021-07-05 Werner Lemberg <wl@gnu.org>
......
......@@ -105,8 +105,7 @@ FT_BEGIN_HEADER
*
* ```
* FREETYPE_PROPERTIES=truetype:interpreter-version=35 \
* cff:no-stem-darkening=1 \
* autofitter:warping=1
* cff:no-stem-darkening=1
* ```
*
*/
......
......@@ -72,6 +72,9 @@ CHANGES BETWEEN 2.10.4 and 2.11.0
This work was Priyesh Kumar's GSoC 2020 project.
- The experimental 'warp' mode (AF_CONFIG_OPTION_USE_WARPER) for the
auto-hinter has been removed.
- The smooth rasterizer performance has been improved by >10%.
- PCF bitmap fonts compressed with LZW (these are usually files with
......
......@@ -105,8 +105,7 @@ FT_BEGIN_HEADER
*
* ```
* FREETYPE_PROPERTIES=truetype:interpreter-version=35 \
* cff:no-stem-darkening=1 \
* autofitter:warping=1
* cff:no-stem-darkening=1
* ```
*
*/
......
......@@ -2113,8 +2113,7 @@ FT_BEGIN_HEADER
* Extra parameters passed to the font driver when opening a new face.
*
* @note:
* The stream type is determined by the contents of `flags` that are
* tested in the following order by @FT_Open_Face:
* The stream type is determined by the contents of `flags`:
*
* If the @FT_OPEN_MEMORY bit is set, assume that this is a memory file
* of `memory_size` bytes, located at `memory_address`. The data are not
......@@ -2127,6 +2126,9 @@ FT_BEGIN_HEADER
* Otherwise, if the @FT_OPEN_PATHNAME bit is set, assume that this is a
* normal file and use `pathname` to open it.
*
* If none of the above bits are set or if multiple are set at the same
* time, the flags are invalid and @FT_Open_Face fails.
*
* If the @FT_OPEN_DRIVER bit is set, @FT_Open_Face only tries to open
* the file with the driver whose handler is in `driver`.
*
......@@ -2299,6 +2301,10 @@ FT_BEGIN_HEADER
* See the discussion of reference counters in the description of
* @FT_Reference_Face.
*
* If `FT_OPEN_STREAM` is set in `args->flags`, the stream in
* `args->stream` is automatically closed before this function returns
* any error (including `FT_Err_Invalid_Argument`).
*
* @example:
* To loop over all faces, use code similar to the following snippet
* (omitting the error handling).
......@@ -3307,13 +3313,13 @@ FT_BEGIN_HEADER
* pixels and use the @FT_PIXEL_MODE_LCD_V mode.
*
* FT_RENDER_MODE_SDF ::
* This mode corresponds to 8-bit signed distance fields (SDF)
* bitmaps. Each pixel in a SDF bitmap contains information about the
* nearest edge of the glyph outline. The distances are calculated
* from the center of the pixel and are positive if they are filled by
* the outline (i.e., inside the outline) and negative otherwise.
* Check the note below on how to convert the output values to usable
* data.
* This mode corresponds to 8-bit, single-channel signed distance field
* (SDF) bitmaps. Each pixel in the SDF grid is the value from the
* pixel's position to the nearest glyph's outline. The distances are
* calculated from the center of the pixel and are positive if they are
* filled by the outline (i.e., inside the outline) and negative
* otherwise. Check the note below on how to convert the output values
* to usable data.
*
* @note:
* The selected render mode only affects vector glyphs of a font.
......
......@@ -53,10 +53,10 @@ FT_BEGIN_HEADER
* reasons.
*
* Available properties are @increase-x-height, @no-stem-darkening
* (experimental), @darkening-parameters (experimental), @warping
* (experimental), @glyph-to-script-map (experimental), @fallback-script
* (experimental), and @default-script (experimental), as documented in
* the @properties section.
* (experimental), @darkening-parameters (experimental),
* @glyph-to-script-map (experimental), @fallback-script (experimental),
* and @default-script (experimental), as documented in the @properties
* section.
*
*/
......@@ -1165,15 +1165,15 @@ FT_BEGIN_HEADER
* **Obsolete**
*
* This property was always experimental and probably never worked
* correctly. It was entirely removed from the FreeType 2 sources.
* This entry is only here for historical reference.
* correctly. It was entirely removed from the FreeType~2 sources. This
* entry is only here for historical reference.
*
* Warping only works in 'normal' auto-hinting mode replacing it. The
* idea of the code is to slightly scale and shift a glyph along the
* Warping only worked in 'normal' auto-hinting mode replacing it. The
* idea of the code was to slightly scale and shift a glyph along the
* non-hinted dimension (which is usually the horizontal axis) so that as
* much of its segments are aligned (more or less) to the grid. To find
* much of its segments were aligned (more or less) to the grid. To find
* out a glyph's optimal scaling and shifting value, various parameter
* combinations are tried and scored.
* combinations were tried and scored.
*
* @since:
* 2.6
......
......@@ -508,8 +508,7 @@ FT_BEGIN_HEADER
*
* ```
* FREETYPE_PROPERTIES=truetype:interpreter-version=35 \
* cff:no-stem-darkening=0 \
* autofitter:warping=1
* cff:no-stem-darkening=0
* ```
*
* @inout:
......
......@@ -71,12 +71,18 @@ FT_BEGIN_HEADER
*/
#define FT_DUMMY_STMNT FT_BEGIN_STMNT FT_END_STMNT
#ifdef _WIN64
#ifdef __UINTPTR_TYPE__
/*
* GCC and Clang both provide a `__UINTPTR_TYPE__` that can be used to
* avoid a dependency on `stdint.h`.
*/
# define FT_UINT_TO_POINTER( x ) (void *)(__UINTPTR_TYPE__)(x)
#elif defined( _WIN64 )
/* only 64bit Windows uses the LLP64 data model, i.e., */
/* 32-bit integers, 64-bit pointers. */
#define FT_UINT_TO_POINTER( x ) (void *)(unsigned __int64)(x)
# define FT_UINT_TO_POINTER( x ) (void *)(unsigned __int64)(x)
#else
#define FT_UINT_TO_POINTER( x ) (void *)(unsigned long)(x)
# define FT_UINT_TO_POINTER( x ) (void *)(unsigned long)(x)
#endif
/*
......
......@@ -160,7 +160,6 @@ FT_TRACE_DEF( afhints )
FT_TRACE_DEF( afmodule )
FT_TRACE_DEF( aflatin )
FT_TRACE_DEF( afshaper )
FT_TRACE_DEF( afwarp )
/* SDF components */
FT_TRACE_DEF( sdf ) /* signed distance raster for outlines (ftsdf.c) */
......
......@@ -193,6 +193,7 @@ has_sys_mman_h = cc.has_header('sys/mman.h')
mmap_option = get_option('mmap')
use_unix_ftsystem_c = false
if mmap_option.disabled()
ft2_sources += files(['src/base/ftsystem.c',])
elif host_machine.system() == 'windows'
......@@ -201,6 +202,7 @@ else
if has_unistd_h and has_fcntl_h and has_sys_mman_h
# This version of `ftsystem.c` uses `mmap` to read input font files.
ft2_sources += files(['builds/unix/ftsystem.c',])
use_unix_ftsystem_c = true
elif mmap_option.enabled()
error('mmap was enabled via options but is not available,'
+ ' required headers were not found!')
......@@ -321,7 +323,7 @@ if has_fcntl_h
ftconfig_command += '--enable=HAVE_FCNTL_H'
endif
if host_machine.system() in ['linux', 'darwin', 'cygwin']
if use_unix_ftsystem_c
ftconfig_h_in = files('builds/unix/ftconfig.h.in')
ftconfig_h = custom_target('ftconfig.h',
input: ftconfig_h_in,
......
......@@ -953,9 +953,6 @@
hints->x_delta = x_delta;
hints->y_delta = y_delta;
hints->xmin_delta = 0;
hints->xmax_delta = 0;
points = hints->points;
if ( hints->num_points == 0 )
goto Exit;
......
......@@ -362,9 +362,6 @@ FT_BEGIN_HEADER
/* implementations */
AF_StyleMetrics metrics;
FT_Pos xmin_delta; /* used for warping */
FT_Pos xmax_delta;
/* Two arrays to avoid allocation penalty. */
/* The `embedded' structure must be the last element! */
struct
......
......@@ -473,8 +473,8 @@
FT_Pos pp2x = loader->pp2.x;
loader->pp1.x = FT_PIX_ROUND( pp1x + hints->xmin_delta );
loader->pp2.x = FT_PIX_ROUND( pp2x + hints->xmax_delta );
loader->pp1.x = FT_PIX_ROUND( pp1x );
loader->pp2.x = FT_PIX_ROUND( pp2x );
slot->lsb_delta = loader->pp1.x - pp1x;
slot->rsb_delta = loader->pp2.x - pp2x;
......
......@@ -197,6 +197,7 @@
FT_Error error;
FT_Memory memory;
FT_Stream stream = NULL;
FT_UInt mode;
*astream = NULL;
......@@ -208,15 +209,15 @@
return FT_THROW( Invalid_Argument );
memory = library->memory;
mode = args->flags &
( FT_OPEN_MEMORY | FT_OPEN_STREAM | FT_OPEN_PATHNAME );
if ( FT_NEW( stream ) )
goto Exit;
stream->memory = memory;
if ( args->flags & FT_OPEN_MEMORY )
if ( mode == FT_OPEN_MEMORY )
{
/* create a memory-based stream */
if ( FT_NEW( stream ) )
goto Exit;
FT_Stream_OpenMemory( stream,
(const FT_Byte*)args->memory_base,
(FT_ULong)args->memory_size );
......@@ -224,33 +225,40 @@
#ifndef FT_CONFIG_OPTION_DISABLE_STREAM_SUPPORT
else if ( args->flags & FT_OPEN_PATHNAME )
else if ( mode == FT_OPEN_PATHNAME )
{
/* create a normal system stream */
if ( FT_NEW( stream ) )
goto Exit;
error = FT_Stream_Open( stream, args->pathname );
stream->pathname.pointer = args->pathname;
if ( error )
FT_FREE( stream );
}
else if ( ( args->flags & FT_OPEN_STREAM ) && args->stream )
else if ( ( mode == FT_OPEN_STREAM ) && args->stream )
{
/* use an existing, user-provided stream */
/* in this case, we do not need to allocate a new stream object */
/* since the caller is responsible for closing it himself */
FT_FREE( stream );
stream = args->stream;
error = FT_Err_Ok;
}
#endif
else
{
error = FT_THROW( Invalid_Argument );
if ( ( args->flags & FT_OPEN_STREAM ) && args->stream )
FT_Stream_Close( args->stream );
}
if ( error )
FT_FREE( stream );
else
stream->memory = memory; /* just to be certain */
*astream = stream;
if ( !error )
{
stream->memory = memory;
*astream = stream;
}
Exit:
return error;
......
......@@ -41,7 +41,8 @@
* file `ftbsdf.c` for more.
*
* * The basic idea of generating the SDF is taken from Viktor Chlumsky's
* research paper.
* research paper. The paper explains both single and multi-channel
* SDF, however, this implementation only generates single-channel SDF.
*
* Chlumsky, Viktor: Shape Decomposition for Multi-channel Distance
* Fields. Master's thesis. Czech Technical University in Prague,
......
......@@ -4,7 +4,7 @@
### Download test fonts
Run the `tests/scripts/download-fonts.sh` script, which will
Run the `tests/scripts/download-fonts.py` script, which will
download test fonts to the `tests/data/` directory first.
### Build the test programs
......
#!/usr/bin/env python3
"""Download test fonts used by the FreeType regression test programs.
These will be copied to $FREETYPE/tests/data/ by default.
"""
import argparse
import collections
import hashlib
import io
import os
import requests
import sys
import zipfile
from typing import Callable, List, Optional, Tuple
# The list of download items describing the font files to install.
# Each download item is a dictionary with one of the following schemas:
#
# - File item:
#
# file_url
# Type: URL string.
# Required: Yes.
# Description: URL to download the file from.
#
# install_name
# Type: file name string
# Required: No
# Description: Installation name for the font file, only provided if it
# must be different from the original URL's basename.
#
# hex_digest
# Type: hexadecimal string
# Required: No
# Description: Digest of the input font file.
#
# - Zip items:
#
# These items correspond to one or more font files that are embedded in a
# remote zip archive. Each entry has the following fields:
#
# zip_url
# Type: URL string.
# Required: Yes.
# Description: URL to download the zip archive from.
#
# zip_files
# Type: List of file entries (see below)
# Required: Yes
# Description: A list of entries describing a single font file to be
# extracted from the archive
#
# Apart from that, some schemas are used for dictionaries used inside download
# items:
#
# - File entries:
#
# These are dictionaries describing a single font file to extract from an archive.
#
# filename
# Type: file path string
# Required: Yes
# Description: Path of source file, relative to the archive's top-level directory.
#
# install_name
# Type: file name string
# Required: No
# Description: Installation name for the font file, only provided if it must be
# different from the original filename value.
#
# hex_digest
# Type: hexadecimal string
# Required: No
# Description: Digest of the input source file
#
_DOWNLOAD_ITEMS = [
{
"zip_url": "https://github.com/python-pillow/Pillow/files/6622147/As.I.Lay.Dying.zip",
"zip_files": [
{
"filename": "As I Lay Dying.ttf",
"install_name": "As.I.Lay.Dying.ttf",
"hex_digest": "ef146bbc2673b387",
},
],
},
]
def digest_data(data: bytes):
"""Compute the digest of a given input byte string, which are the first 8 bytes of its sha256 hash."""
m = hashlib.sha256()
m.update(data)
return m.digest()[:8]
def check_existing(path: str, hex_digest: str):
"""Return True if |path| exists and matches |hex_digest|."""
if not os.path.exists(path) or hex_digest is None:
return False
with open(path, "rb") as f:
existing_content = f.read()
return bytes.fromhex(hex_digest) == digest_data(existing_content)
def install_file(content: bytes, dest_path: str):
"""Write a byte string to a given destination file.
Args:
content: Input data, as a byte string
dest_path: Installation path
"""
parent_path = os.path.dirname(dest_path)
if not os.path.exists(parent_path):
os.makedirs(parent_path)
with open(dest_path, "wb") as f:
f.write(content)
def download_file(url: str, expected_digest: Optional[bytes] = None):
"""Download a file from a given URL.
Args:
url: Input URL
expected_digest: Optional digest of the file
as a byte string
Returns:
URL content as binary string.
"""
r = requests.get(url, allow_redirects=True)
content = r.content
if expected_digest is not None:
digest = digest_data(r.content)
if digest != expected_digest:
raise ValueError(
"%s has invalid digest %s (expected %s)"
% (url, digest.hex(), expected_digest.hex())
)
return content
def extract_file_from_zip_archive(
archive: zipfile.ZipFile,
archive_name: str,
filepath: str,
expected_digest: Optional[bytes] = None,
):
"""Extract a file from a given zipfile.ZipFile archive.
Args:
archive: Input ZipFile objec.
archive_name: Archive name or URL, only used to generate a human-readable error
message.
filepath: Input filepath in archive.
expected_digest: Optional digest for the file.
Returns:
A new File instance corresponding to the extract file.
Raises:
ValueError if expected_digest is not None and does not match the extracted file.
"""
file = archive.open(filepath)
if expected_digest is not None:
digest = digest_data(archive.open(filepath).read())
if digest != expected_digest:
raise ValueError(
"%s in zip archive at %s has invalid digest %s (expected %s)"
% (filepath, archive_name, digest.hex(), expected_digest.hex())
)
return file.read()
def _get_and_install_file(
install_path: str,
hex_digest: Optional[str],
force_download: bool,
get_content: Callable[[], bytes],
) -> bool:
if not force_download and hex_digest is not None and os.path.exists(install_path):
with open(install_path, "rb") as f:
content: bytes = f.read()
if bytes.fromhex(hex_digest) == digest_data(content):
return False
content = get_content()
install_file(content, install_path)
return True
def download_and_install_item(
item: dict, install_dir: str, force_download: bool
) -> List[Tuple[str, bool]]:
"""Download and install one item.
Args:
item: Download item as a dictionary, see above for schema.
install_dir: Installation directory.
force_download: Set to True to force download and installation, even if
the font file is already installed with the right content.
Returns:
A list of (install_name, status) tuples, where 'install_name' is the file's
installation name under 'install_dir', and 'status' is a boolean that is True
to indicate that the file was downloaded and installed, or False to indicate that
the file is already installed with the right content.
"""
if "file_url" in item:
file_url = item["file_url"]
install_name = item.get("install_name", os.path.basename(file_url))
install_path = os.path.join(install_dir, install_name)
hex_digest = item.get("hex_digest")
def get_content():
return download_file(file_url, hex_digest)
status = _get_and_install_file(
install_path, hex_digest, force_download, get_content
)
return [(install_name, status)]
if "zip_url" in item:
# One or more files from a zip archive.
archive_url = item["zip_url"]
archive = zipfile.ZipFile(io.BytesIO(download_file(archive_url)))
result = []
for f in item["zip_files"]:
filename = f["filename"]
install_name = f.get("install_name", filename)
hex_digest = f.get("hex_digest")
def get_content():
return extract_file_from_zip_archive(
archive,
archive_url,
filename,
bytes.fromhex(hex_digest) if hex_digest else None,
)
status = _get_and_install_file(
os.path.join(install_dir, install_name),
hex_digest,
force_download,
get_content,
)
result.append((install_name, status))
return result
else:
raise ValueError("Unknown download item schema: %s" % item)
def main():
parser = argparse.ArgumentParser(description=__doc__)
# Assume this script is under tests/scripts/ and tests/data/
# is the default installation directory.
install_dir = os.path.normpath(
os.path.join(os.path.dirname(__file__), "..", "data")
)
parser.add_argument(
"--force",
action="store_true",
default=False,
help="Force download and installation of font files",
)
parser.add_argument(
"--install-dir",
default=install_dir,
help="Specify installation directory [%s]" % install_dir,
)
args = parser.parse_args()
for item in _DOWNLOAD_ITEMS:
for install_name, status in download_and_install_item(
item, args.install_dir, args.force
):
print("%s %s" % (install_name, "INSTALLED" if status else "UP-TO-DATE"))
return 0
if __name__ == "__main__":
sys.exit(main())
#!/usr/bin/bash
# Download test fonts used by the FreeType regression test programs.
# These will be copied to $FREETYPE/tests/data/
# Each font file contains an 8-hexchar prefix corresponding to its md5sum
set -e
export LANG=C
export LC_ALL=C
PROGDIR=$(dirname "$0")
PROGNAME=$(basename "$0")
# Download a file from a given URL
#
# $1: URL
# $2: Destination directory
# $3: If not empty, destination file name. Default is to take
# the URL's basename.
#
download_file () {
local URL=$1
local DST_DIR=$2
local DST_FILE=$3
if [[ -z "$DST_FILE" ]]; then
DST_FILE=$(basename "$URL")
fi
echo "URL: $URL"
wget -q -O "$DST_DIR/$DST_FILE" "$URL"
}
# $1: URL
# $2: Destination directory
# $3+: Optional file list, otherwise the full archive is extracted to $2
download_and_extract_zip () {
local URL=$1
local DST_DIR=$2
shift
shift
TEMP_DST_DIR=$(mktemp -d)
TEMP_DST_NAME="a.zip"
download_file "$URL" "$TEMP_DST_DIR" "$TEMP_DST_NAME"
unzip -qo "$TEMP_DST_DIR/$TEMP_DST_NAME" -d "$DST_DIR" "$@"
rm -rf "$TEMP_DST_DIR"
}
# $1: File path
# $2: Expected md5sum
md5sum_check () {
local FILE=$1
local EXPECTED=$2
local HASH=$(md5sum "$FILE" | cut -d" " -f1)
if [[ "$EXPECTED" != "$HASH" ]]; then
echo "$FILE: Invalid md5sum $HASH expected $EXPECTED"
return 1
fi
}
INSTALL_DIR=$(cd $PROGDIR/.. && pwd)/data
mkdir -p "$INSTALL_DIR"
# See https://gitlab.freedesktop.org/freetype/freetype/-/issues/1063
download_and_extract_zip "https://github.com/python-pillow/Pillow/files/6622147/As.I.Lay.Dying.zip" "$INSTALL_DIR"
mv "$INSTALL_DIR/As I Lay Dying.ttf" "$INSTALL_DIR/As.I.Lay.Dying.ttf"
md5sum_check "$INSTALL_DIR/As.I.Lay.Dying.ttf" e153d60e66199660f7cfe99ef4705ad7