提交 c8b230e4 编写于 作者: R Rod Sheeter

Merge branch 'expand-tests' of github.com:googlefonts/harfbuzz into drophints

......@@ -2,11 +2,17 @@
for f in $(find . -name '*.log' -not -name 'config.log'); do
last=$(tail -1 $f)
if [[ $last = FAIL* || $last = *failed* ]]; then
if [[ $last = FAIL* ]]; then
echo '====' $f '===='
cat $f
elif [[ $last = PASS* ]]; then
# Do nothing.
true
else
# Travis Linux images has an old automake that does not match the
# patterns above, so in case of doubt just print the file.
cat $f
fi
done
# Intentionally exiting with non-zero.
exit 1
......@@ -37,6 +37,21 @@ jobs:
- run: make
- run: make check || .ci/fail.sh
clang-O3-O0:
docker:
- image: multiarch/crossbuild
steps:
- checkout
- run: apt update && apt install -y ragel libfreetype6-dev libglib2.0-dev libcairo2-dev libicu-dev libgraphite2-dev python python-pip
- run: pip install fonttools
- run: wget http://download.savannah.gnu.org/releases/freetype/freetype-2.9.tar.bz2 && tar xf freetype-2.9.tar.bz2 && cd freetype-2.9 && ./autogen.sh && ./configure && make -j4 && cd ..
- run: CFLAGS="-O3" CXXFLAGS="-O3" CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-glib --with-cairo --with-icu --with-graphite2
- run: make
- run: LD_LIBRARY_PATH="$PWD/freetype-2.9/objs/.libs" make check || .ci/fail.sh
- run: CFLAGS="-O0" CXXFLAGS="-O0" CC=clang CXX=clang++ ./autogen.sh --with-freetype --with-glib --with-cairo --with-icu --with-graphite2
- run: make
- run: LD_LIBRARY_PATH="$PWD/freetype-2.9/objs/.libs" make check || .ci/fail.sh
fedora-outoftreebuild:
docker:
- image: fedora
......@@ -153,6 +168,7 @@ workflows:
# autotools based builds
- alpine-O3
- archlinux-debug-O0
- clang-O3-O0
- fedora-outoftreebuild
# cmake based builds
......
......@@ -6,8 +6,8 @@ language: cpp
env:
global:
- CPPFLAGS=""
- CFLAGS="-Werror --coverage"
- CXXFLAGS="-Werror -Wno-deprecated-register --coverage" # glib uses register and clang raises a warning
- CFLAGS="-Werror -Werror=unused-function --coverage"
- CXXFLAGS="-Werror -Werror=unused-function -Wno-deprecated-register --coverage" # glib uses register and clang raises a warning
- LDFLAGS="--coverage"
- CONFIGURE_OPTS="--with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-graphite2"
- NOCONFIGURE=1
......
......@@ -137,7 +137,7 @@ set (IN_HB_DIST FALSE)
if (EXISTS "${PROJECT_SOURCE_DIR}/ChangeLog")
# perhaps we are on dist directory
set (IN_HB_DIST TRUE)
set (HB_VERSION_H "${PROJECT_SOURCE_DIR}/src/hb-version.h")
#set (HB_VERSION_H "${PROJECT_SOURCE_DIR}/src/hb-version.h")
endif ()
......@@ -180,13 +180,13 @@ add_prefix_to_list(HB_SUBSET_headers "${PROJECT_SOURCE_DIR}/src/")
extract_make_variable(HB_BASE_RAGEL_GENERATED_sources ${SRCSOURCES})
extract_make_variable(HB_OT_RAGEL_GENERATED_sources ${SRCSOURCES})
if (IN_HB_DIST)
#if (IN_HB_DIST)
add_prefix_to_list(HB_BASE_RAGEL_GENERATED_sources "${PROJECT_SOURCE_DIR}/src/")
add_prefix_to_list(HB_OT_RAGEL_GENERATED_sources "${PROJECT_SOURCE_DIR}/src/")
else ()
add_prefix_to_list(HB_BASE_RAGEL_GENERATED_sources "${PROJECT_BINARY_DIR}/src/")
add_prefix_to_list(HB_OT_RAGEL_GENERATED_sources "${PROJECT_BINARY_DIR}/src/")
endif ()
#else ()
# add_prefix_to_list(HB_BASE_RAGEL_GENERATED_sources "${PROJECT_BINARY_DIR}/src/")
# add_prefix_to_list(HB_OT_RAGEL_GENERATED_sources "${PROJECT_BINARY_DIR}/src/")
#endif ()
extract_make_variable(HB_VIEW_sources ${UTILSOURCES})
add_prefix_to_list(HB_VIEW_sources "${PROJECT_SOURCE_DIR}/util/")
......@@ -234,17 +234,17 @@ endif ()
## Generate hb-version.h
if (NOT IN_HB_DIST)
set (HB_VERSION_H_IN "${PROJECT_SOURCE_DIR}/src/hb-version.h.in")
set (HB_VERSION_H "${PROJECT_BINARY_DIR}/src/hb-version.h")
set_source_files_properties("${HB_VERSION_H}" PROPERTIES GENERATED true)
configure_file("${HB_VERSION_H_IN}" "${HB_VERSION_H}.tmp" @ONLY)
execute_process(COMMAND "${CMAKE_COMMAND}" -E copy_if_different
"${HB_VERSION_H}.tmp"
"${HB_VERSION_H}"
)
file(REMOVE "${HB_VERSION_H}.tmp")
endif ()
#if (NOT IN_HB_DIST)
# set (HB_VERSION_H_IN "${PROJECT_SOURCE_DIR}/src/hb-version.h.in")
# set (HB_VERSION_H "${PROJECT_BINARY_DIR}/src/hb-version.h")
# set_source_files_properties("${HB_VERSION_H}" PROPERTIES GENERATED true)
# configure_file("${HB_VERSION_H_IN}" "${HB_VERSION_H}.tmp" @ONLY)
# execute_process(COMMAND "${CMAKE_COMMAND}" -E copy_if_different
# "${HB_VERSION_H}.tmp"
# "${HB_VERSION_H}"
# )
# file(REMOVE "${HB_VERSION_H}.tmp")
#endif ()
## Define sources and headers of the project
......@@ -264,7 +264,7 @@ set (subset_project_sources
set (project_extra_sources)
set (project_headers
${HB_VERSION_H}
#${HB_VERSION_H}
${HB_BASE_headers}
${HB_OT_headers}
......@@ -809,7 +809,7 @@ endif ()
## src/ executables
if (NOT HB_DISABLE_TEST_PROGS)
foreach (prog main test test-would-substitute test-size-params test-buffer-serialize hb-ot-tag)
foreach (prog main test test-would-substitute test-size-params test-buffer-serialize hb-ot-tag test-unicode-ranges)
set (prog_name ${prog})
if (${prog_name} STREQUAL "test")
# test can not be used as a valid executable name on cmake, lets special case it
......
......@@ -30,7 +30,6 @@ HBDEPS =
HBSOURCES = $(HB_BASE_sources)
HBSOURCES += $(HB_BASE_RAGEL_GENERATED_sources)
HBHEADERS = $(HB_BASE_headers)
HBNODISTHEADERS = $(HB_NODIST_headers)
if WITH_LIBSTDCXX
HBNOLIBCXXCFLAGS =
......@@ -147,13 +146,13 @@ endif
base_link_flags = $(AM_LDFLAGS) -lm -version-info $(HB_LIBTOOL_VERSION_INFO) -no-undefined
libharfbuzz_la_LINK = $(chosen_linker) $(libharfbuzz_la_LDFLAGS)
libharfbuzz_la_SOURCES = $(HBSOURCES) $(HBHEADERS) $(HBNODISTHEADERS)
libharfbuzz_la_SOURCES = $(HBSOURCES) $(HBHEADERS)
libharfbuzz_la_CPPFLAGS = $(HBCFLAGS) $(HBNOLIBCXXFLAGS)
libharfbuzz_la_LDFLAGS = $(base_link_flags) $(export_symbols)
libharfbuzz_la_LIBADD = $(HBLIBS)
EXTRA_libharfbuzz_la_DEPENDENCIES = $(harfbuzz_def_dependency)
pkginclude_HEADERS = $(HBHEADERS)
nodist_pkginclude_HEADERS = $(HBNODISTHEADERS)
nodist_pkginclude_HEADERS =
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = harfbuzz.pc
cmakedir = $(libdir)/cmake/harfbuzz
......@@ -382,17 +381,21 @@ dump_use_data_SOURCES = dump-use-data.cc hb-ot-shape-complex-use-table.cc
dump_use_data_CPPFLAGS = $(HBCFLAGS)
dump_use_data_LDADD = libharfbuzz.la $(HBLIBS)
check_PROGRAMS += test-ot-tag
TESTS += test-ot-tag
check_PROGRAMS += test-ot-tag test-unicode-ranges
TESTS += test-ot-tag test-unicode-ranges
test_ot_tag_SOURCES = hb-ot-tag.cc
test_ot_tag_CPPFLAGS = $(HBCFLAGS) -DMAIN
test_ot_tag_LDADD = libharfbuzz.la $(HBLIBS)
test_unicode_ranges_SOURCES = test-unicode-ranges.cc
test_unicode_ranges_LDADD = libharfbuzz.la $(HBLIBS)
TESTS_ENVIRONMENT = \
srcdir="$(srcdir)" \
MAKE="$(MAKE) $(AM_MAKEFLAGS)" \
HBSOURCES="$(HBSOURCES)" \
HBHEADERS="$(HBHEADERS) $(HBNODISTHEADERS)" \
HBHEADERS="$(HBHEADERS)" \
$(NULL)
if HAVE_INTROSPECTION
......@@ -422,7 +425,6 @@ HarfBuzz_0_0_gir_LIBS = \
$(NULL)
HarfBuzz_0_0_gir_FILES = \
$(HBHEADERS) \
$(HBNODISTHEADERS) \
$(HBSOURCES) \
$(HB_GOBJECT_sources) \
$(HB_GOBJECT_headers) \
......
......@@ -28,6 +28,7 @@ HB_BASE_sources = \
hb-ot-maxp-table.hh \
hb-ot-name-table.hh \
hb-ot-os2-table.hh \
hb-ot-os2-unicode-ranges.hh \
hb-ot-post-macroman.hh \
hb-ot-post-table.hh \
hb-ot-tag.cc \
......@@ -70,9 +71,6 @@ HB_BASE_headers = \
hb-shape.h \
hb-shape-plan.h \
hb-unicode.h \
$(NULL)
HB_NODIST_headers = \
hb-version.h \
$(NULL)
......@@ -83,12 +81,14 @@ HB_FALLBACK_sources = \
HB_OT_sources = \
hb-aat-layout.cc \
hb-aat-layout-common-private.hh \
hb-aat-layout-morx-table.hh \
hb-aat-layout-ankr-table.hh \
hb-aat-layout-kerx-table.hh \
hb-aat-layout-morx-table.hh \
hb-aat-layout-trak-table.hh \
hb-aat-layout-private.hh \
hb-ot-font.cc \
hb-ot-layout.cc \
hb-ot-layout-base-table.hh \
hb-ot-layout-common-private.hh \
hb-ot-layout-gdef-table.hh \
hb-ot-layout-gpos-table.hh \
......@@ -152,6 +152,7 @@ HB_OT_headers = \
hb-ot-font.h \
hb-ot-layout.h \
hb-ot-math.h \
hb-ot-base.h \
hb-ot-shape.h \
hb-ot-tag.h \
hb-ot-var.h \
......
......@@ -9,14 +9,28 @@
# $ cmake -DHB_CHECK=ON -Bbuild -H. -GNinja && ninja -Cbuild
# $ src/dev-run.sh [FONT-FILE] [TEXT]
#
# If you are using iTerm2, issue the script like this:
# $ src/dev-run.sh img [FONT-FILE] [TEXT]
#
[ $# = 0 ] && echo Usage: "src/dev-run.sh [FONT-FILE] [TEXT]" && exit
command -v entr >/dev/null 2>&1 || { echo >&2 "This script needs `entr` be installed"; exit 1; }
GDB=gdb
# if gdb doesn't exist, hopefully lldb exist
command -v $GDB >/dev/null 2>&1 || export GDB="lldb"
[ $1 = "img" ] && img=1 && shift
# http://iterm2.com/documentation-images.html
osc="\033]"
if [[ $TERM == screen* ]]; then osc="\033Ptmux;\033\033]"; fi
st="\a"
if [[ $TERM == screen* ]]; then st="\a"; fi
tmp=$(mktemp)
[ -f 'build/build.ninja' ] && CMAKENINJA=TRUE
# or "fswatch -0 . -e build/ -e .git"
find src/ | entr printf '\0' | while read -d ""; do
......@@ -25,19 +39,29 @@ find src/ | entr printf '\0' | while read -d ""; do
if [[ $CMAKENINJA ]]; then
ninja -Cbuild hb-shape hb-view && {
build/hb-shape $@
build/hb-view $@
if [ $img ]; then
build/hb-view $@ -O png -o $tmp
printf "\n${osc}1337;File=;inline=1:`cat $tmp | base64`${st}\n"
else
build/hb-view $@
fi
}
else
make -Cbuild/src -j5 -s lib && {
build/util/hb-shape $@
build/util/hb-view $@
if [ $img ]; then
build/util/hb-view $@ -O png -o $tmp
printf "\n${osc}1337;File=;inline=1:`cat $tmp | base64`${st}\n"
else
build/util/hb-view $@
fi
}
fi
done
read -n 1 -p "[T]est, [D]ebug, [R]estart, [Q]uit?" answer
read -n 1 -p "[C]heck, [D]ebug, [R]estart, [Q]uit? " answer
case "$answer" in
t|T )
c|C )
if [[ $CMAKENINJA ]]; then
CTEST_OUTPUT_ON_FAILURE=1 CTEST_PARALLEL_LEVEL=5 ninja -Cbuild test
else
......@@ -48,7 +72,7 @@ d|D )
if [[ $CMAKENINJA ]]; then
echo "Not supported on cmake builds yet"
else
build/libtool --mode=execute $GDB build/util/hb-shape $@
build/libtool --mode=execute $GDB -- build/util/hb-shape $@
fi
;;
r|R )
......
# -*- coding: utf-8 -*-
# Generates the code for a sorted unicode range array as used in hb-ot-os2-unicode-ranges.hh
# Input is a tab seperated list of unicode ranges from the otspec
# (https://docs.microsoft.com/en-us/typography/opentype/spec/os2#ulunicoderange1).
import io
import re
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
print (u"""static Range os2UnicodeRangesSorted[] =
{""")
args = sys.argv[1:]
input_file = args[0]
with io.open(input_file, mode="r", encoding="utf-8") as f:
all_ranges = [];
current_bit = 0
while True:
line = f.readline().strip()
if not line:
break
fields = re.split(r'\t+', line)
if len(fields) == 3:
current_bit = fields[0]
fields = fields[1:]
elif len(fields) > 3:
raise Error("bad input :(.")
name = fields[0]
ranges = re.split("-", fields[1])
if len(ranges) != 2:
raise Error("bad input :(.")
v = tuple((int(ranges[0], 16), int(ranges[1], 16), int(current_bit), name))
all_ranges.append(v)
all_ranges = sorted(all_ranges, key=lambda t: t[0])
for ranges in all_ranges:
start = ("0x%X" % ranges[0]).rjust(8)
end = ("0x%X" % ranges[1]).rjust(8)
bit = ("%s" % ranges[2]).rjust(3)
print " {%s, %s, %s}, // %s" % (start, end, bit, ranges[3])
print (u"""};""");
/*
* Copyright © 2018 Ebrahim Byagowi
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
#ifndef HB_AAT_LAYOUT_ANKR_TABLE_HH
#define HB_AAT_LAYOUT_ANKR_TABLE_HH
#include "hb-aat-layout-common-private.hh"
#define HB_AAT_TAG_ankr HB_TAG('a','n','k','r')
namespace AAT {
/*
* ankr -- Anchor point
*/
struct Anchor
{
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this));
}
FWORD xCoordinate;
FWORD yCoordinate;
public:
DEFINE_SIZE_STATIC (4);
};
struct ankr
{
static const hb_tag_t tableTag = HB_AAT_TAG_ankr;
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) && version == 0 &&
lookupTable.sanitize (c, this) &&
anchors.sanitize (c, this));
}
protected:
HBUINT16 version; /* Version number (set to zero) */
HBUINT16 flags; /* Flags (currently unused; set to zero) */
LOffsetTo<Lookup<HBUINT16> > lookupTable; /* Offset to the table's lookup table */
LOffsetTo<ArrayOf<Anchor, HBUINT32> >
anchors; /* Offset to the glyph data table */
public:
DEFINE_SIZE_STATIC (12);
};
} /* namespace AAT */
#endif /* HB_AAT_LAYOUT_ANKR_TABLE_HH */
......@@ -263,6 +263,13 @@ struct kerx
{
static const hb_tag_t tableTag = HB_AAT_TAG_kerx;
inline bool apply (hb_aat_apply_context_t *c, const AAT::ankr *ankr) const
{
TRACE_APPLY (this);
/* TODO */
return_trace (false);
}
struct SubTableWrapper
{
enum coverage_flags_t {
......
......@@ -37,4 +37,7 @@
HB_INTERNAL void
hb_aat_layout_substitute (hb_font_t *font, hb_buffer_t *buffer);
HB_INTERNAL void
hb_aat_layout_position (hb_font_t *font, hb_buffer_t *buffer);
#endif /* HB_AAT_LAYOUT_PRIVATE_HH */
/*
* Copyright © 2018 Google, Inc.
* Copyright © 2018 Ebrahim Byagowi
* Copyright © 2018 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
......@@ -39,16 +39,26 @@ namespace AAT {
struct TrackTableEntry
{
inline bool sanitize (hb_sanitize_context_t *c) const
inline bool sanitize (hb_sanitize_context_t *c, const void *base, unsigned int size) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this));
return_trace (c->check_struct (this) && (values.sanitize (c, base, size)));
}
inline float get_track_value () const
{
return track.to_float ();
}
inline int get_value (const void *base, unsigned int index) const
{
return (base+values)[index];
}
protected:
Fixed track; /* Track value for this record. */
HBUINT16 trackNameID; /* The 'name' table index for this track */
OffsetTo<UnsizedArrayOf<Fixed> >
OffsetTo<UnsizedArrayOf<FWORD> >
values; /* Offset from start of tracking table to
* per-size tracking values for this track. */
......@@ -58,18 +68,66 @@ struct TrackTableEntry
struct TrackData
{
inline bool sanitize (hb_sanitize_context_t *c) const
inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this));
return_trace (c->check_struct (this) &&
sizeTable.sanitize (c, base, nSizes) &&
trackTable.sanitize (c, nTracks, base, nSizes));
}
inline float get_tracking (const void *base, float ptem) const
{
/* CoreText points are CSS pixels (96 per inch),
* NOT typographic points (72 per inch).
*
* https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html
*/
float csspx = ptem * 96.f / 72.f;
Fixed fixed_size;
fixed_size.set_float (csspx);
/* XXX Clean this up. Make it work with nSizes==1 and 0. */
unsigned int sizes = nSizes;
const TrackTableEntry *trackTableEntry = nullptr;
for (unsigned int i = 0; i < sizes; ++i)
// For now we only seek for track entries with zero tracking value
if (trackTable[i].get_track_value () == 0.)
trackTableEntry = &trackTable[0];
// We couldn't match any, exit
if (!trackTableEntry) return 0.;
/* TODO bfind() */
unsigned int size_index;
UnsizedArrayOf<Fixed> size_table = base+sizeTable;
for (size_index = 0; size_index < sizes; ++size_index)
if (size_table[size_index] >= fixed_size)
break;
// TODO(ebraminio): We don't attempt to extrapolate to larger or
// smaller values for now but we should do, per spec
if (size_index == sizes)
return trackTableEntry->get_value (base, sizes - 1);
if (size_index == 0 || size_table[size_index] == fixed_size)
return trackTableEntry->get_value (base, size_index);
float s0 = size_table[size_index - 1].to_float ();
float s1 = size_table[size_index].to_float ();
float t = (csspx - s0) / (s1 - s0);
return t * trackTableEntry->get_value (base, size_index) +
(1.0 - t) * trackTableEntry->get_value (base, size_index - 1);
}
protected:
HBUINT16 nTracks; /* Number of separate tracks included in this table. */
HBUINT16 nSizes; /* Number of point sizes included in this table. */
LOffsetTo<UnsizedArrayOf<Fixed> >
LOffsetTo<UnsizedArrayOf<Fixed> > /* Offset to array[nSizes] of size values. */
sizeTable;
TrackTableEntry trackTable[VAR];/* Array[nSizes] of size values. */
UnsizedArrayOf<TrackTableEntry>
trackTable; /* Array[nTracks] of TrackTableEntry records. */
public:
DEFINE_SIZE_ARRAY (8, trackTable);
......@@ -82,15 +140,55 @@ struct trak
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this));
return_trace (c->check_struct (this) &&
horizData.sanitize (c, this, this) &&
vertData.sanitize (c, this, this));
}
inline bool apply (hb_aat_apply_context_t *c) const
{
TRACE_APPLY (this);
const float ptem = c->font->ptem;
if (ptem <= 0.f)
return_trace (false);
hb_buffer_t *buffer = c->buffer;
if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
{
const TrackData &trackData = this+horizData;
float tracking = trackData.get_tracking (this, ptem);
hb_position_t advance_to_add = c->font->em_scalef_x (tracking / 2);
foreach_grapheme (buffer, start, end)
{
/* TODO This is wrong. */
buffer->pos[start].x_advance += advance_to_add;
buffer->pos[end].x_advance += advance_to_add;
}
}
else
{
const TrackData &trackData = this+vertData;
float tracking = trackData.get_tracking (this, ptem);
hb_position_t advance_to_add = c->font->em_scalef_y (tracking / 2);
foreach_grapheme (buffer, start, end)
{
/* TODO This is wrong. */
buffer->pos[start].y_advance += advance_to_add;
buffer->pos[end].y_advance += advance_to_add;
}
}
return_trace (true);
}
protected:
FixedVersion<> version; /* Version of the tracking table--currently
* 0x00010000u for version 1.0. */
HBUINT16 format; /* Format of the tracking table */
OffsetTo<TrackData> horizOffset; /* TrackData for horizontal text */
OffsetTo<TrackData> vertOffset; /* TrackData for vertical text */
OffsetTo<TrackData> horizData; /* TrackData for horizontal text */
OffsetTo<TrackData> vertData; /* TrackData for vertical text */
HBUINT16 reserved; /* Reserved. Set to 0. */
public:
......
......@@ -30,14 +30,48 @@
#include "hb-ot-layout-gsubgpos-private.hh"
#include "hb-aat-layout-private.hh"
#include "hb-aat-layout-morx-table.hh"
#include "hb-aat-layout-ankr-table.hh"
#include "hb-aat-layout-kerx-table.hh"
#include "hb-aat-layout-morx-table.hh"
#include "hb-aat-layout-trak-table.hh"
/*
* mort/morx
* morx/kerx/trak
*/
static inline const AAT::ankr&
_get_ankr (hb_face_t *face, hb_blob_t **blob = nullptr)
{
if (unlikely (!hb_ot_shaper_face_data_ensure (face)))
{
if (blob)
*blob = hb_blob_get_empty ();
return OT::Null(AAT::ankr);
}
hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
const AAT::ankr& ankr = *(layout->ankr.get ());
if (blob)
*blob = layout->ankr.blob;
return ankr;
}
static inline const AAT::kerx&
_get_kerx (hb_face_t *face, hb_blob_t **blob = nullptr)
{
if (unlikely (!hb_ot_shaper_face_data_ensure (face)))
{
if (blob)
*blob = hb_blob_get_empty ();
return OT::Null(AAT::kerx);
}
hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
/* XXX this doesn't call set_num_glyphs on sanitizer. */
const AAT::kerx& kerx = *(layout->kerx.get ());
if (blob)
*blob = layout->kerx.blob;
return kerx;
}
static inline const AAT::morx&
_get_morx (hb_face_t *face, hb_blob_t **blob = nullptr)
{
......@@ -55,20 +89,36 @@ _get_morx (hb_face_t *face, hb_blob_t **blob = nullptr)
return morx;
}
static inline void
_hb_aat_layout_create (hb_face_t *face)
static inline const AAT::trak&
_get_trak (hb_face_t *face, hb_blob_t **blob = nullptr)
{
OT::Sanitizer<AAT::morx> sanitizer;
sanitizer.set_num_glyphs (face->get_num_glyphs ());
hb_blob_t *morx_blob = sanitizer.sanitize (face->reference_table (HB_AAT_TAG_MORX));
OT::Sanitizer<AAT::morx>::lock_instance (morx_blob);
if (0)
if (unlikely (!hb_ot_shaper_face_data_ensure (face)))
{
OT::Sanitizer<AAT::Lookup<OT::GlyphID> >::lock_instance (morx_blob)->get_value (1, face->get_num_glyphs ());
if (blob)
*blob = hb_blob_get_empty ();
return OT::Null(AAT::trak);
}
hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
const AAT::trak& trak = *(layout->trak.get ());
if (blob)
*blob = layout->trak.blob;
return trak;
}
// static inline void
// _hb_aat_layout_create (hb_face_t *face)
// {
// OT::Sanitizer<AAT::morx> sanitizer;
// sanitizer.set_num_glyphs (face->get_num_glyphs ());
// hb_blob_t *morx_blob = sanitizer.sanitize (face->reference_table (HB_AAT_TAG_MORX));
// OT::Sanitizer<AAT::morx>::lock_instance (morx_blob);
// if (0)
// {
// OT::Sanitizer<AAT::Lookup<OT::GlyphID> >::lock_instance (morx_blob)->get_value (1, face->get_num_glyphs ());
// }
// }
void
hb_aat_layout_substitute (hb_font_t *font, hb_buffer_t *buffer)
{
......@@ -78,3 +128,16 @@ hb_aat_layout_substitute (hb_font_t *font, hb_buffer_t *buffer)
AAT::hb_aat_apply_context_t c (font, buffer, blob);
morx.apply (&c);
}
void
hb_aat_layout_position (hb_font_t *font, hb_buffer_t *buffer)
{
hb_blob_t *blob;
const AAT::ankr& ankr = _get_ankr (font->face, &blob);
const AAT::kerx& kerx = _get_kerx (font->face, &blob);
const AAT::trak& trak = _get_trak (font->face, &blob);
AAT::hb_aat_apply_context_t c (font, buffer, blob);
kerx.apply (&c, &ankr);
trak.apply (&c);
}
......@@ -692,8 +692,8 @@ struct F2DOT14 : HBINT16
/* 32-bit signed fixed-point number (16.16). */
struct Fixed: HBINT32
{
//inline float to_float (void) const { return ???; }
//inline void set_float (float f) { v.set (f * ???); }
inline float to_float (void) const { return ((int32_t) v) / 65536.0; }
inline void set_float (float f) { v.set (round (f * 65536.0)); }
public:
DEFINE_SIZE_STATIC (4);
};
......@@ -740,8 +740,6 @@ template <typename Type>
struct Offset : Type
{
inline bool is_null (void) const { return 0 == *this; }
public:
DEFINE_SIZE_STATIC (sizeof(Type));
inline void *serialize (hb_serialize_context_t *c, const void *base)
{
......@@ -749,6 +747,9 @@ struct Offset : Type
this->set ((char *) t - (char *) base); /* TODO(serialize) Overflow? */
return t;
}
public:
DEFINE_SIZE_STATIC (sizeof(Type));
};
typedef Offset<HBUINT16> Offset16;
......
/*
* Copyright © 2017 Elie Roux<elie.roux@telecom-bretagne.eu>
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
*/
#ifndef HB_OT_H_IN
#error "Include <hb-ot.h> instead."
#endif
#ifndef HB_OT_BASE_H
#define HB_OT_BASE_H
#include "hb.h"
HB_BEGIN_DECLS
#define HB_OT_TAG_BASE HB_TAG('B','A','S','E')
// https://www.microsoft.com/typography/otspec/baselinetags.htm
#define HB_OT_TAG_BASE_HANG HB_TAG('h','a','n','g')
#define HB_OT_TAG_BASE_ICFB HB_TAG('i','c','f','b')
#define HB_OT_TAG_BASE_ICFT HB_TAG('i','c','f','t')
#define HB_OT_TAG_BASE_IDEO HB_TAG('i','d','e','o')
#define HB_OT_TAG_BASE_IDTB HB_TAG('i','d','t','b')
#define HB_OT_TAG_BASE_MATH HB_TAG('m','a','t','h')
#define HB_OT_TAG_BASE_ROMN HB_TAG('r','o','m','n')
/* Methods */
// HB_EXTERN hb_bool_t
// hb_ot_base_has_data (hb_face_t *face);
HB_END_DECLS
#endif /* HB_OT_BASE_H */
此差异已折叠。
......@@ -110,7 +110,7 @@ struct CaretValueFormat1
protected:
HBUINT16 caretValueFormat; /* Format identifier--format = 1 */
HBINT16 coordinate; /* X or Y value, in design units */
FWORD coordinate; /* X or Y value, in design units */
public:
DEFINE_SIZE_STATIC (4);
};
......@@ -161,7 +161,7 @@ struct CaretValueFormat3
protected:
HBUINT16 caretValueFormat; /* Format identifier--format = 3 */
HBINT16 coordinate; /* X or Y value, in design units */
FWORD coordinate; /* X or Y value, in design units */
OffsetTo<Device>
deviceTable; /* Offset to Device table for X or Y
* value--from beginning of CaretValue
......
......@@ -248,8 +248,8 @@ struct AnchorFormat1
protected:
HBUINT16 format; /* Format identifier--format = 1 */
HBINT16 xCoordinate; /* Horizontal value--in design units */
HBINT16 yCoordinate; /* Vertical value--in design units */
FWORD xCoordinate; /* Horizontal value--in design units */
FWORD yCoordinate; /* Vertical value--in design units */
public:
DEFINE_SIZE_STATIC (6);
};
......@@ -279,8 +279,8 @@ struct AnchorFormat2
protected:
HBUINT16 format; /* Format identifier--format = 2 */
HBINT16 xCoordinate; /* Horizontal value--in design units */
HBINT16 yCoordinate; /* Vertical value--in design units */
FWORD xCoordinate; /* Horizontal value--in design units */
FWORD yCoordinate; /* Vertical value--in design units */
HBUINT16 anchorPoint; /* Index to glyph contour point */
public:
DEFINE_SIZE_STATIC (8);
......@@ -309,8 +309,8 @@ struct AnchorFormat3
protected:
HBUINT16 format; /* Format identifier--format = 3 */
HBINT16 xCoordinate; /* Horizontal value--in design units */
HBINT16 yCoordinate; /* Vertical value--in design units */
FWORD xCoordinate; /* Horizontal value--in design units */
FWORD yCoordinate; /* Vertical value--in design units */
OffsetTo<Device>
xDeviceTable; /* Offset to Device table for X
* coordinate-- from beginning of
......
......@@ -114,7 +114,7 @@ struct SingleSubstFormat1
OffsetTo<Coverage>
coverage; /* Offset to Coverage table--from
* beginning of Substitution table */
HBINT16 deltaGlyphID; /* Add to original GlyphID to get
HBINT16 deltaGlyphID; /* Add to original GlyphID to get
* substitute GlyphID */
public:
DEFINE_SIZE_STATIC (6);
......
......@@ -122,6 +122,7 @@ hb_ot_layout_position_finish_offsets (hb_font_t *font,
*/
namespace OT {
struct BASE;
struct GDEF;
struct GSUB;
struct GPOS;
......@@ -131,8 +132,9 @@ namespace OT {
}
namespace AAT {
struct morx;
struct ankr;
struct kerx;
struct morx;
struct trak;
}
......@@ -168,11 +170,13 @@ struct hb_ot_layout_t
const struct OT::GPOS *gpos;
/* TODO Move the following out of this struct. */
OT::hb_lazy_table_loader_t<struct OT::BASE> base;
OT::hb_lazy_table_loader_t<struct OT::MATH> math;
OT::hb_lazy_table_loader_t<struct OT::fvar> fvar;
OT::hb_lazy_table_loader_t<struct OT::avar> avar;
OT::hb_lazy_table_loader_t<struct AAT::morx> morx;
OT::hb_lazy_table_loader_t<struct AAT::ankr> ankr;
OT::hb_lazy_table_loader_t<struct AAT::kerx> kerx;
OT::hb_lazy_table_loader_t<struct AAT::morx> morx;
OT::hb_lazy_table_loader_t<struct AAT::trak> trak;
unsigned int gsub_lookup_count;
......@@ -363,6 +367,28 @@ _hb_glyph_info_get_modified_combining_class (const hb_glyph_info_t *info)
return _hb_glyph_info_is_unicode_mark (info) ? info->unicode_props()>>8 : 0;
}
/* Loop over grapheme. Based on foreach_cluster(). */
#define foreach_grapheme(buffer, start, end) \
for (unsigned int \
_count = buffer->len, \
start = 0, end = _count ? _next_grapheme (buffer, 0) : 0; \
start < _count; \
start = end, end = _next_grapheme (buffer, start))
static inline unsigned int
_next_grapheme (hb_buffer_t *buffer, unsigned int start)
{
hb_glyph_info_t *info = buffer->info;
unsigned int count = buffer->len;
while (++start < count && _hb_glyph_info_is_unicode_mark (&info[start]))
;
return start;
}
#define info_cc(info) (_hb_glyph_info_get_modified_combining_class (&(info)))
static inline bool
......
......@@ -31,6 +31,7 @@
#include "hb-open-type-private.hh"
#include "hb-ot-layout-private.hh"
#include "hb-ot-layout-base-table.hh"
#include "hb-ot-layout-gdef-table.hh"
#include "hb-ot-layout-gsub-table.hh"
#include "hb-ot-layout-gpos-table.hh"
......@@ -62,9 +63,13 @@ _hb_ot_layout_create (hb_face_t *face)
layout->gpos = OT::Sanitizer<OT::GPOS>::lock_instance (layout->gpos_blob);
layout->math.init (face);
layout->base.init (face);
layout->fvar.init (face);
layout->avar.init (face);
layout->ankr.init (face);
layout->kerx.init (face);
layout->morx.init (face);
layout->trak.init (face);
{
/*
......@@ -212,13 +217,25 @@ _hb_ot_layout_destroy (hb_ot_layout_t *layout)
hb_blob_destroy (layout->gpos_blob);
layout->math.fini ();
layout->base.fini ();
layout->fvar.fini ();
layout->avar.fini ();
layout->ankr.fini ();
layout->kerx.fini ();
layout->morx.fini ();
layout->trak.fini ();
free (layout);
}
// static inline const OT::BASE&
// _get_base (hb_face_t *face)
// {
// if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::BASE);
// hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
// return *(layout->base.get ());
// }
static inline const OT::GDEF&
_get_gdef (hb_face_t *face)
{
......@@ -1264,3 +1281,27 @@ hb_ot_layout_substitute_lookup (OT::hb_ot_apply_context_t *c,
{
apply_string<GSUBProxy> (c, lookup, accel);
}
/*
* OT::BASE
*/
// /**
// * hb_ot_base_has_data:
// * @face: #hb_face_t to test
// *
// * This function allows to verify the presence of an OpenType BASE table on the
// * face.
// *
// * Return value: true if face has a BASE table, false otherwise
// *
// * Since: XXX
// **/
// hb_bool_t
// hb_ot_base_has_data (hb_face_t *face)
// {
// return &_get_base (face) != &OT::Null(OT::BASE);
// }
......@@ -38,6 +38,7 @@
HB_BEGIN_DECLS
#define HB_OT_TAG_BASE HB_TAG('B','A','S','E')
#define HB_OT_TAG_GDEF HB_TAG('G','D','E','F')
#define HB_OT_TAG_GSUB HB_TAG('G','S','U','B')
#define HB_OT_TAG_GPOS HB_TAG('G','P','O','S')
......
......@@ -39,9 +39,39 @@ namespace OT {
#define HB_OT_TAG_maxp HB_TAG('m','a','x','p')
struct maxpV1Tail
{
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this));
}
HBUINT16 maxPoints; /* Maximum points in a non-composite glyph. */
HBUINT16 maxContours; /* Maximum contours in a non-composite glyph. */
HBUINT16 maxCompositePoints; /* Maximum points in a composite glyph. */
HBUINT16 maxCompositeContours; /* Maximum contours in a composite glyph. */
HBUINT16 maxZones; /* 1 if instructions do not use the twilight zone (Z0),
* or 2 if instructions do use Z0; should be set to 2 in
* most cases. */
HBUINT16 maxTwilightPoints; /* Maximum points used in Z0. */
HBUINT16 maxStorage; /* Number of Storage Area locations. */
HBUINT16 maxFunctionDefs; /* Number of FDEFs, equal to the highest function number + 1. */
HBUINT16 maxInstructionDefs; /* Number of IDEFs. */
HBUINT16 maxStackElements; /* Maximum stack depth. (This includes Font and CVT
* Programs, as well as the instructions for each glyph.) */
HBUINT16 maxSizeOfInstructions; /* Maximum byte count for glyph instructions. */
HBUINT16 maxComponentElements; /* Maximum number of components referenced at
* "top level" for any composite glyph. */
HBUINT16 maxComponentDepth; /* Maximum levels of recursion; 1 for simple components. */
public:
DEFINE_SIZE_STATIC (26);
};
struct maxp
{
static const hb_tag_t tableTag = HB_OT_TAG_maxp;
static const hb_tag_t tableTag = HB_OT_TAG_maxp;
inline unsigned int get_num_glyphs (void) const
{
......@@ -56,9 +86,15 @@ struct maxp
inline bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (c->check_struct (this) &&
likely (version.major == 1 ||
(version.major == 0 && version.minor == 0x5000u)));
if (unlikely (!c->check_struct (this)))
return_trace (false);
if (version.major == 1)
{
const maxpV1Tail &v1 = StructAfter<maxpV1Tail> (*this);
return v1.sanitize (c);
}
return_trace (likely (version.major == 0 && version.minor == 0x5000u));
}
inline bool subset (hb_subset_plan_t *plan) const
......@@ -73,17 +109,34 @@ struct maxp
OT::maxp *maxp_prime = (OT::maxp *) hb_blob_get_data (maxp_prime_blob, nullptr);
maxp_prime->set_num_glyphs (plan->gids_to_retain_sorted.len);
if (plan->drop_hints)
drop_hint_fields (plan, maxp_prime);
bool result = hb_subset_plan_add_table(plan, HB_OT_TAG_maxp, maxp_prime_blob);
hb_blob_destroy (maxp_prime_blob);
return result;
}
/* We only implement version 0.5 as none of the extra fields in version 1.0 are useful. */
static inline void drop_hint_fields (hb_subset_plan_t *plan, OT::maxp *maxp_prime)
{
if (maxp_prime->version.major == 1)
{
maxpV1Tail &v1 = StructAfter<maxpV1Tail> (*maxp_prime);
v1.maxZones.set (1);
v1.maxTwilightPoints.set (0);
v1.maxStorage.set (0);
v1.maxFunctionDefs.set (0);
v1.maxInstructionDefs.set (0);
v1.maxStackElements.set (0);
v1.maxSizeOfInstructions.set (0);
}
}
protected:
FixedVersion<>version; /* Version of the maxp table (0.5 or 1.0),
* 0x00005000u or 0x00010000u. */
HBUINT16 numGlyphs; /* The number of glyphs in the font. */
/*maxpV1Tail v1Tail[VAR]; */
public:
DEFINE_SIZE_STATIC (6);
};
......
......@@ -28,7 +28,7 @@
#define HB_OT_OS2_TABLE_HH
#include "hb-open-type-private.hh"
#include "hb-ot-os2-unicode-ranges.hh"
namespace OT {
......@@ -67,11 +67,40 @@ struct os2
os2_prime->usFirstCharIndex.set (min_cp);
os2_prime->usLastCharIndex.set (max_cp);
_update_unicode_ranges (plan->codepoints, os2_prime->ulUnicodeRange);
bool result = hb_subset_plan_add_table(plan, HB_OT_TAG_os2, os2_prime_blob);
hb_blob_destroy (os2_prime_blob);
return result;
}
inline void _update_unicode_ranges (const hb_prealloced_array_t<hb_codepoint_t> &codepoints,
HBUINT32 ulUnicodeRange[4]) const
{
for (unsigned int i = 0; i < 4; i++)
ulUnicodeRange[i].set (0);
for (unsigned int i = 0; i < codepoints.len; i++)
{
hb_codepoint_t cp = codepoints[i];
unsigned int bit = hb_get_unicode_range_bit (cp);
if (bit < 128)
{
unsigned int block = bit / 32;
unsigned int bit_in_block = bit % 32;
unsigned int mask = 1 << bit_in_block;
ulUnicodeRange[block].set (ulUnicodeRange[block] | mask);
}
if (cp >= 0x10000 && cp <= 0x110000)
{
/* the spec says that bit 57 ("Non Plane 0") implies that there's
at least one codepoint beyond the BMP; so I also include all
the non-BMP codepoints here */
ulUnicodeRange[1].set (ulUnicodeRange[1] | (1 << 25));
}
}
}
static inline void find_min_and_max_codepoint (const hb_prealloced_array_t<hb_codepoint_t> &codepoints,
uint16_t *min_cp, /* OUT */
uint16_t *max_cp /* OUT */)
......
/*
* Copyright © 2018 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Garret Rieger
*/
#ifndef HB_OT_OS2_UNICODE_RANGES_HH
#define HB_OT_OS2_UNICODE_RANGES_HH
#include "hb-private.hh"
#include "hb-dsalgs.hh"
namespace OT {
struct Range {
hb_codepoint_t start;
hb_codepoint_t end;
unsigned int bit;
};
/* Note: The contents of this array was generated using src/gen-unicode-ranges.py. */
static Range os2UnicodeRangesSorted[] =
{
{ 0x0, 0x7F, 0}, // Basic Latin
{ 0x80, 0xFF, 1}, // Latin-1 Supplement
{ 0x100, 0x17F, 2}, // Latin Extended-A
{ 0x180, 0x24F, 3}, // Latin Extended-B
{ 0x250, 0x2AF, 4}, // IPA Extensions
{ 0x2B0, 0x2FF, 5}, // Spacing Modifier Letters
{ 0x300, 0x36F, 6}, // Combining Diacritical Marks
{ 0x370, 0x3FF, 7}, // Greek and Coptic
{ 0x400, 0x4FF, 9}, // Cyrillic
{ 0x500, 0x52F, 9}, // Cyrillic Supplement
{ 0x530, 0x58F, 10}, // Armenian
{ 0x590, 0x5FF, 11}, // Hebrew
{ 0x600, 0x6FF, 13}, // Arabic
{ 0x700, 0x74F, 71}, // Syriac
{ 0x750, 0x77F, 13}, // Arabic Supplement
{ 0x780, 0x7BF, 72}, // Thaana
{ 0x7C0, 0x7FF, 14}, // NKo
{ 0x900, 0x97F, 15}, // Devanagari
{ 0x980, 0x9FF, 16}, // Bengali
{ 0xA00, 0xA7F, 17}, // Gurmukhi
{ 0xA80, 0xAFF, 18}, // Gujarati
{ 0xB00, 0xB7F, 19}, // Oriya
{ 0xB80, 0xBFF, 20}, // Tamil
{ 0xC00, 0xC7F, 21}, // Telugu
{ 0xC80, 0xCFF, 22}, // Kannada
{ 0xD00, 0xD7F, 23}, // Malayalam
{ 0xD80, 0xDFF, 73}, // Sinhala
{ 0xE00, 0xE7F, 24}, // Thai
{ 0xE80, 0xEFF, 25}, // Lao
{ 0xF00, 0xFFF, 70}, // Tibetan
{ 0x1000, 0x109F, 74}, // Myanmar
{ 0x10A0, 0x10FF, 26}, // Georgian
{ 0x1100, 0x11FF, 28}, // Hangul Jamo
{ 0x1200, 0x137F, 75}, // Ethiopic
{ 0x1380, 0x139F, 75}, // Ethiopic Supplement
{ 0x13A0, 0x13FF, 76}, // Cherokee
{ 0x1400, 0x167F, 77}, // Unified Canadian Aboriginal Syllabics
{ 0x1680, 0x169F, 78}, // Ogham
{ 0x16A0, 0x16FF, 79}, // Runic
{ 0x1700, 0x171F, 84}, // Tagalog
{ 0x1720, 0x173F, 84}, // Hanunoo
{ 0x1740, 0x175F, 84}, // Buhid
{ 0x1760, 0x177F, 84}, // Tagbanwa
{ 0x1780, 0x17FF, 80}, // Khmer
{ 0x1800, 0x18AF, 81}, // Mongolian
{ 0x1900, 0x194F, 93}, // Limbu
{ 0x1950, 0x197F, 94}, // Tai Le
{ 0x1980, 0x19DF, 95}, // New Tai Lue
{ 0x19E0, 0x19FF, 80}, // Khmer Symbols
{ 0x1A00, 0x1A1F, 96}, // Buginese
{ 0x1B00, 0x1B7F, 27}, // Balinese
{ 0x1B80, 0x1BBF, 112}, // Sundanese
{ 0x1C00, 0x1C4F, 113}, // Lepcha
{ 0x1C50, 0x1C7F, 114}, // Ol Chiki
{ 0x1D00, 0x1D7F, 4}, // Phonetic Extensions
{ 0x1D80, 0x1DBF, 4}, // Phonetic Extensions Supplement
{ 0x1DC0, 0x1DFF, 6}, // Combining Diacritical Marks Supplement
{ 0x1E00, 0x1EFF, 29}, // Latin Extended Additional
{ 0x1F00, 0x1FFF, 30}, // Greek Extended
{ 0x2000, 0x206F, 31}, // General Punctuation
{ 0x2070, 0x209F, 32}, // Superscripts And Subscripts
{ 0x20A0, 0x20CF, 33}, // Currency Symbols
{ 0x20D0, 0x20FF, 34}, // Combining Diacritical Marks For Symbols
{ 0x2100, 0x214F, 35}, // Letterlike Symbols
{ 0x2150, 0x218F, 36}, // Number Forms
{ 0x2190, 0x21FF, 37}, // Arrows
{ 0x2200, 0x22FF, 38}, // Mathematical Operators
{ 0x2300, 0x23FF, 39}, // Miscellaneous Technical
{ 0x2400, 0x243F, 40}, // Control Pictures
{ 0x2440, 0x245F, 41}, // Optical Character Recognition
{ 0x2460, 0x24FF, 42}, // Enclosed Alphanumerics
{ 0x2500, 0x257F, 43}, // Box Drawing
{ 0x2580, 0x259F, 44}, // Block Elements
{ 0x25A0, 0x25FF, 45}, // Geometric Shapes
{ 0x2600, 0x26FF, 46}, // Miscellaneous Symbols
{ 0x2700, 0x27BF, 47}, // Dingbats
{ 0x27C0, 0x27EF, 38}, // Miscellaneous Mathematical Symbols-A
{ 0x27F0, 0x27FF, 37}, // Supplemental Arrows-A
{ 0x2800, 0x28FF, 82}, // Braille Patterns
{ 0x2900, 0x297F, 37}, // Supplemental Arrows-B
{ 0x2980, 0x29FF, 38}, // Miscellaneous Mathematical Symbols-B
{ 0x2A00, 0x2AFF, 38}, // Supplemental Mathematical Operators
{ 0x2B00, 0x2BFF, 37}, // Miscellaneous Symbols and Arrows
{ 0x2C00, 0x2C5F, 97}, // Glagolitic
{ 0x2C60, 0x2C7F, 29}, // Latin Extended-C
{ 0x2C80, 0x2CFF, 8}, // Coptic
{ 0x2D00, 0x2D2F, 26}, // Georgian Supplement
{ 0x2D30, 0x2D7F, 98}, // Tifinagh
{ 0x2D80, 0x2DDF, 75}, // Ethiopic Extended
{ 0x2DE0, 0x2DFF, 9}, // Cyrillic Extended-A
{ 0x2E00, 0x2E7F, 31}, // Supplemental Punctuation
{ 0x2E80, 0x2EFF, 59}, // CJK Radicals Supplement
{ 0x2F00, 0x2FDF, 59}, // Kangxi Radicals
{ 0x2FF0, 0x2FFF, 59}, // Ideographic Description Characters
{ 0x3000, 0x303F, 48}, // CJK Symbols And Punctuation
{ 0x3040, 0x309F, 49}, // Hiragana
{ 0x30A0, 0x30FF, 50}, // Katakana
{ 0x3100, 0x312F, 51}, // Bopomofo
{ 0x3130, 0x318F, 52}, // Hangul Compatibility Jamo
{ 0x3190, 0x319F, 59}, // Kanbun
{ 0x31A0, 0x31BF, 51}, // Bopomofo Extended
{ 0x31C0, 0x31EF, 61}, // CJK Strokes
{ 0x31F0, 0x31FF, 50}, // Katakana Phonetic Extensions
{ 0x3200, 0x32FF, 54}, // Enclosed CJK Letters And Months
{ 0x3300, 0x33FF, 55}, // CJK Compatibility
{ 0x3400, 0x4DBF, 59}, // CJK Unified Ideographs Extension A
{ 0x4DC0, 0x4DFF, 99}, // Yijing Hexagram Symbols
{ 0x4E00, 0x9FFF, 59}, // CJK Unified Ideographs
{ 0xA000, 0xA48F, 83}, // Yi Syllables
{ 0xA490, 0xA4CF, 83}, // Yi Radicals
{ 0xA500, 0xA63F, 12}, // Vai
{ 0xA640, 0xA69F, 9}, // Cyrillic Extended-B
{ 0xA700, 0xA71F, 5}, // Modifier Tone Letters
{ 0xA720, 0xA7FF, 29}, // Latin Extended-D
{ 0xA800, 0xA82F, 100}, // Syloti Nagri
{ 0xA840, 0xA87F, 53}, // Phags-pa
{ 0xA880, 0xA8DF, 115}, // Saurashtra
{ 0xA900, 0xA92F, 116}, // Kayah Li
{ 0xA930, 0xA95F, 117}, // Rejang
{ 0xAA00, 0xAA5F, 118}, // Cham
{ 0xAC00, 0xD7AF, 56}, // Hangul Syllables
{ 0xD800, 0xDFFF, 57}, // Non-Plane 0 *
{ 0xE000, 0xF8FF, 60}, // Private Use Area (plane 0)
{ 0xF900, 0xFAFF, 61}, // CJK Compatibility Ideographs
{ 0xFB00, 0xFB4F, 62}, // Alphabetic Presentation Forms
{ 0xFB50, 0xFDFF, 63}, // Arabic Presentation Forms-A
{ 0xFE00, 0xFE0F, 91}, // Variation Selectors
{ 0xFE10, 0xFE1F, 65}, // Vertical Forms
{ 0xFE20, 0xFE2F, 64}, // Combining Half Marks
{ 0xFE30, 0xFE4F, 65}, // CJK Compatibility Forms
{ 0xFE50, 0xFE6F, 66}, // Small Form Variants
{ 0xFE70, 0xFEFF, 67}, // Arabic Presentation Forms-B
{ 0xFF00, 0xFFEF, 68}, // Halfwidth And Fullwidth Forms
{ 0xFFF0, 0xFFFF, 69}, // Specials
{ 0x10000, 0x1007F, 101}, // Linear B Syllabary
{ 0x10080, 0x100FF, 101}, // Linear B Ideograms
{ 0x10100, 0x1013F, 101}, // Aegean Numbers
{ 0x10140, 0x1018F, 102}, // Ancient Greek Numbers
{ 0x10190, 0x101CF, 119}, // Ancient Symbols
{ 0x101D0, 0x101FF, 120}, // Phaistos Disc
{ 0x10280, 0x1029F, 121}, // Lycian
{ 0x102A0, 0x102DF, 121}, // Carian
{ 0x10300, 0x1032F, 85}, // Old Italic
{ 0x10330, 0x1034F, 86}, // Gothic
{ 0x10380, 0x1039F, 103}, // Ugaritic
{ 0x103A0, 0x103DF, 104}, // Old Persian
{ 0x10400, 0x1044F, 87}, // Deseret
{ 0x10450, 0x1047F, 105}, // Shavian
{ 0x10480, 0x104AF, 106}, // Osmanya
{ 0x10800, 0x1083F, 107}, // Cypriot Syllabary
{ 0x10900, 0x1091F, 58}, // Phoenician
{ 0x10920, 0x1093F, 121}, // Lydian
{ 0x10A00, 0x10A5F, 108}, // Kharoshthi
{ 0x12000, 0x123FF, 110}, // Cuneiform
{ 0x12400, 0x1247F, 110}, // Cuneiform Numbers and Punctuation
{ 0x1D000, 0x1D0FF, 88}, // Byzantine Musical Symbols
{ 0x1D100, 0x1D1FF, 88}, // Musical Symbols
{ 0x1D200, 0x1D24F, 88}, // Ancient Greek Musical Notation
{ 0x1D300, 0x1D35F, 109}, // Tai Xuan Jing Symbols
{ 0x1D360, 0x1D37F, 111}, // Counting Rod Numerals
{ 0x1D400, 0x1D7FF, 89}, // Mathematical Alphanumeric Symbols
{ 0x1F000, 0x1F02F, 122}, // Mahjong Tiles
{ 0x1F030, 0x1F09F, 122}, // Domino Tiles
{ 0x20000, 0x2A6DF, 59}, // CJK Unified Ideographs Extension B
{ 0x2F800, 0x2FA1F, 61}, // CJK Compatibility Ideographs Supplement
{ 0xE0000, 0xE007F, 92}, // Tags
{ 0xE0100, 0xE01EF, 91}, // Variation Selectors Supplement
{ 0xF0000, 0xFFFFD, 90}, // Private Use (plane 15)
{0x100000, 0x10FFFD, 90}, // Private Use (plane 16)
};
static int
_compare_range (const void *_key, const void *_item, void *_arg)
{
hb_codepoint_t cp = *((hb_codepoint_t *) _key);
const Range *range = (Range *) _item;
if (cp < range->start)
return -1;
else if (cp <= range->end)
return 0;
else
return 1;
}
/**
* hb_get_unicode_range_bit:
* Returns the bit to be set in os/2 ulUnicodeRange for a given codepoint.
**/
static unsigned int
hb_get_unicode_range_bit (hb_codepoint_t cp)
{
Range *range = (Range*) hb_bsearch_r (&cp, os2UnicodeRangesSorted,
sizeof (os2UnicodeRangesSorted) / sizeof(Range),
sizeof(Range),
_compare_range, nullptr);
if (range != NULL)
return range->bit;
return -1;
}
} /* namespace OT */
#endif /* HB_OT_OS2_UNICODE_RANGES_HH */
......@@ -787,6 +787,8 @@ hb_ot_position (hb_ot_shape_context_t *c)
_hb_ot_shape_fallback_kern (c->plan, c->font, c->buffer);
_hb_buffer_deallocate_gsubgpos_vars (c->buffer);
//hb_aat_layout_position (c->font, c->buffer);
}
static inline void
......
......@@ -33,6 +33,7 @@
#include "hb-ot-font.h"
#include "hb-ot-layout.h"
#include "hb-ot-math.h"
#include "hb-ot-base.h"
#include "hb-ot-tag.h"
#include "hb-ot-shape.h"
#include "hb-ot-var.h"
......
......@@ -136,7 +136,7 @@ _hb_subset_face_data_destroy (void *user_data)
{
hb_subset_face_data_t *data = (hb_subset_face_data_t *) user_data;
for (int i = 0; i < data->tables.len; i++)
for (unsigned int i = 0; i < data->tables.len; i++)
hb_blob_destroy (data->tables[i].blob);
data->tables.finish ();
......
/*
* Copyright © 2011 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Behdad Esfahbod
*/
#ifndef HB_H_IN
#error "Include <hb.h> instead."
#endif
#ifndef HB_VERSION_H
#define HB_VERSION_H
#include "hb-common.h"
HB_BEGIN_DECLS
#define HB_VERSION_MAJOR 1
#define HB_VERSION_MINOR 7
#define HB_VERSION_MICRO 5
#define HB_VERSION_STRING "1.7.5"
#define HB_VERSION_ATLEAST(major,minor,micro) \
((major)*10000+(minor)*100+(micro) <= \
HB_VERSION_MAJOR*10000+HB_VERSION_MINOR*100+HB_VERSION_MICRO)
HB_EXTERN void
hb_version (unsigned int *major,
unsigned int *minor,
unsigned int *micro);
HB_EXTERN const char *
hb_version_string (void);
HB_EXTERN hb_bool_t
hb_version_atleast (unsigned int major,
unsigned int minor,
unsigned int micro);
HB_END_DECLS
#endif /* HB_VERSION_H */
/*
* Copyright © 2018 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Garret Rieger
*/
#include "hb-private.hh"
#include "hb-ot-os2-unicode-ranges.hh"
void
test (hb_codepoint_t cp, int bit)
{
if (OT::hb_get_unicode_range_bit (cp) != bit)
{
fprintf (stderr, "got incorrect bit (%d) for cp 0x%X. Should have been %d.",
OT::hb_get_unicode_range_bit (cp),
cp,
bit);
abort();
}
}
void
test_get_unicode_range_bit (void)
{
test (0x0000, 0);
test (0x0042, 0);
test (0x007F, 0);
test (0x0080, 1);
test (0x30A0, 50);
test (0x30B1, 50);
test (0x30FF, 50);
test (0x10FFFD, 90);
test (0x30000, -1);
test (0x110000, -1);
}
int
main (void)
{
test_get_unicode_range_bit ();
return 0;
}
......@@ -121,15 +121,16 @@ test_set_basic (void)
hb_set_destroy (s);
}
static inline void
print_set (hb_set_t *s)
{
hb_codepoint_t next;
printf ("{");
for (next = HB_SET_VALUE_INVALID; hb_set_next (s, &next); )
printf ("%d, ", next);
printf ("}\n");
}
// static inline void
// print_set (hb_set_t *s)
// {
// hb_codepoint_t next;
// printf ("{");
// for (next = HB_SET_VALUE_INVALID; hb_set_next (s, &next); )
// printf ("%d, ", next);
// printf ("}\n");
// }
static void
test_set_algebra (void)
......
......@@ -31,14 +31,30 @@
/* Unit tests for hb-subset-glyf.h */
static void check_maxp_num_glyphs (hb_face_t *face, uint16_t expected_num_glyphs)
static void check_maxp_field (uint8_t *raw_maxp, unsigned int offset, uint16_t expected_value)
{
uint16_t actual_value = (raw_maxp[offset] << 8) + raw_maxp[offset + 1];
g_assert_cmpuint(expected_value, ==, actual_value);
}
static void check_maxp_num_glyphs (hb_face_t *face, uint16_t expected_num_glyphs, bool hints)
{
hb_blob_t *maxp_blob = hb_face_reference_table (face, HB_TAG ('m','a','x', 'p'));
unsigned int maxp_len;
uint8_t *raw_maxp = (uint8_t *) hb_blob_get_data(maxp_blob, &maxp_len);
uint16_t num_glyphs = (raw_maxp[4] << 8) + raw_maxp[5];
g_assert_cmpuint(expected_num_glyphs, ==, num_glyphs);
check_maxp_field (raw_maxp, 4, expected_num_glyphs); // numGlyphs
if (!hints)
{
check_maxp_field (raw_maxp, 14, 1); // maxZones
check_maxp_field (raw_maxp, 16, 0); // maxTwilightPoints
check_maxp_field (raw_maxp, 18, 0); // maxStorage
check_maxp_field (raw_maxp, 20, 0); // maxFunctionDefs
check_maxp_field (raw_maxp, 22, 0); // maxInstructionDefs
check_maxp_field (raw_maxp, 24, 0); // maxStackElements
check_maxp_field (raw_maxp, 26, 0); // maxSizeOfInstructions
}
hb_blob_destroy (maxp_blob);
}
......@@ -57,7 +73,7 @@ test_subset_glyf (void)
hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('g','l','y','f'));
hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('l','o','c', 'a'));
check_maxp_num_glyphs(face_abc_subset, 3);
check_maxp_num_glyphs(face_abc_subset, 3, true);
hb_face_destroy (face_abc_subset);
hb_face_destroy (face_abc);
......@@ -77,7 +93,7 @@ test_subset_glyf_with_components (void)
hb_subset_test_check (face_subset, face_generated_subset, HB_TAG ('g','l','y','f'));
hb_subset_test_check (face_subset, face_generated_subset, HB_TAG ('l','o','c', 'a'));
check_maxp_num_glyphs(face_generated_subset, 4);
check_maxp_num_glyphs(face_generated_subset, 4, true);
hb_face_destroy (face_generated_subset);
hb_face_destroy (face_subset);
......@@ -98,7 +114,7 @@ test_subset_glyf_noop (void)
hb_subset_test_check (face_abc, face_abc_subset, HB_TAG ('g','l','y','f'));
hb_subset_test_check (face_abc, face_abc_subset, HB_TAG ('l','o','c', 'a'));
check_maxp_num_glyphs(face_abc_subset, 4);
check_maxp_num_glyphs(face_abc_subset, 4, true);
hb_face_destroy (face_abc_subset);
hb_face_destroy (face_abc);
......@@ -120,7 +136,7 @@ test_subset_glyf_strip_hints_simple (void)
hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('l','o','c', 'a'));
hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('g','l','y','f'));
check_maxp_num_glyphs(face_abc_subset, 3);
check_maxp_num_glyphs(face_abc_subset, 3, false);
hb_face_destroy (face_abc_subset);
hb_face_destroy (face_abc);
......
......@@ -8,6 +8,7 @@ SUBDIRS =
EXTRA_DIST = \
$(TESTS) \
expected/basics \
expected/full-font \
fonts \
profiles \
$(NULL)
......
TESTS = \
tests/basics.tests \
tests/full-font.tests \
$(NULL)
XFAIL_TESTS = \
......
......@@ -3,6 +3,11 @@ Roboto-Regular.abc.ttf
PROFILES:
default.txt
drop-hints.txt
SUBSETS:
abc
b
c
ac
a
FONTS:
Roboto-Regular.ttf
PROFILES:
default.txt
drop-hints.txt
SUBSETS:
abc
Ǽ!A bc
......@@ -15,12 +15,17 @@ def usage():
print "Usage: generate-expected-outputs.py <test suite file> ..."
def generate_expected_output(input_file, unicodes, output_path):
check_call(["fonttools", "subset",
input_file,
"--drop-tables+=DSIG,GPOS,GSUB,GDEF",
"--unicodes=%s" % unicodes,
"--output-file=%s" % output_path])
def generate_expected_output(input_file, unicodes, profile_flags, output_path):
args = ["fonttools", "subset", input_file]
args.extend(profile_flags)
args.extend(["--notdef-outline",
"--name-IDs=*",
"--name-languages=*",
"--name-legacy",
"--drop-tables+=DSIG,GPOS,GSUB,GDEF",
"--unicodes=%s" % unicodes,
"--output-file=%s" % output_path])
check_call(args)
args = sys.argv[1:]
......@@ -37,6 +42,6 @@ for path in args:
unicodes = test.unicodes()
font_name = test.get_font_name()
print "Creating subset %s/%s" % (output_directory, font_name)
generate_expected_output(test.font_path, unicodes,
os.path.join(output_directory,
font_name))
generate_expected_output(test.font_path, unicodes, test.get_profile_flags(),
os.path.join(output_directory,
font_name))
......@@ -44,6 +44,7 @@ def run_test(test):
"--font-file=" + test.font_path,
"--output-file=" + out_file,
"--unicodes=%s" % test.unicodes()]
cli_args.extend (test.get_profile_flags())
print (' '.join(cli_args))
_, return_code = cmd(cli_args)
......@@ -78,7 +79,7 @@ def run_ttx(file):
def strip_check_sum (ttx_string):
return re.sub ('checkSumAdjustment value=["]0x([0-9a-fA-F])+["]',
'checkSumAdjustment value="0x00000000"',
'checkSumAdjustment value="0x00000000"',
ttx_string, count=1)
args = sys.argv[1:]
......
#!/usr/bin/env python
import io
import os
# A single test in a subset test suite. Identifies a font
......@@ -13,15 +14,19 @@ class Test:
def unicodes(self):
return ",".join("%X" % ord(c) for (i, c) in enumerate(self.subset))
def get_profile_flags(self):
with io.open(self.profile_path, mode="r", encoding="utf-8") as f:
return f.read().splitlines();
def get_font_name(self):
font_base_name = os.path.basename(self.font_path)
font_base_name_parts = os.path.splitext(font_base_name)
profile_name = os.path.splitext(os.path.basename(self.profile_path))[0]
return "%s.%s.%s%s" % (font_base_name_parts[0],
profile_name,
self.unicodes(),
font_base_name_parts[1])
profile_name,
self.unicodes(),
font_base_name_parts[1])
# A group of tests to perform on the subsetter. Each test
# Identifies a font a subsetting profile, and a subset to be cut.
......
......@@ -454,7 +454,7 @@ struct font_options_t : option_group_t
default_font_size = default_font_size_;
x_ppem = 0;
y_ppem = 0;
ptem = .0;
ptem = 0.;
subpixel_bits = subpixel_bits_;
font_file = nullptr;
face_index = 0;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册