blob: 8a4d7dd2626113ca058d8434d0227280753e734b [file]
/*
* Copyright © 2022 Behdad Esfahbod
*
* 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.
*
*/
#include "hb.hh"
#include "hb-serialize.hh"
#include "hb-ot-layout-common.hh"
#include "hb-ot-layout-gsubgpos.hh"
#include "OT/Layout/GPOS/ValueFormat.hh"
using OT::Layout::Common::Coverage;
static void
test_null_condition ()
{
static const char and_condition[sizeof (OT::Condition)] = {
0, 3, 1, /* format, conditionCount */
0, 0, 0, /* conditionOffset */
};
static const char or_condition[sizeof (OT::Condition)] = {
0, 4, 1, /* format, conditionCount */
0, 0, 0, /* conditionOffset */
};
static const char negate_condition[sizeof (OT::Condition)] = {
0, 5, /* format */
0, 0, 0, /* conditionOffset */
};
const auto &condition = Null (OT::Condition);
const auto &condition_and =
*reinterpret_cast<const OT::Condition *> (and_condition);
const auto &condition_or =
*reinterpret_cast<const OT::Condition *> (or_condition);
const auto &condition_negate =
*reinterpret_cast<const OT::Condition *> (negate_condition);
OT::ItemVarStoreInstancer *instancer = nullptr;
hb_set_t var_indices;
hb_always_assert (condition.evaluate (nullptr, 0, instancer));
hb_always_assert (condition_and.evaluate (nullptr, 0, instancer));
hb_always_assert (condition_or.evaluate (nullptr, 0, instancer));
hb_always_assert (!condition_negate.evaluate (nullptr, 0, instancer));
hb_always_assert (condition.collect_var_indices (&var_indices));
hb_always_assert (condition_and.collect_var_indices (&var_indices));
hb_always_assert (condition_or.collect_var_indices (&var_indices));
hb_always_assert (condition_negate.collect_var_indices (&var_indices));
hb_always_assert (var_indices.is_empty ());
char buf[8];
hb_serialize_context_t s (buf, sizeof (buf));
hb_map_t varidx_map;
static const unsigned char expected[] = {
0, 2, /* format */
0, 1, /* defaultValue */
0xFF, 0xFF, 0xFF, 0xFF, /* varIdx */
};
auto *copy = s.start_serialize<OT::Condition> ();
hb_always_assert (copy->serialize (&s, &condition, varidx_map));
s.end_serialize ();
hb_bytes_t bytes = s.copy_bytes ();
hb_always_assert (bytes.length == sizeof (expected));
hb_always_assert (!hb_memcmp (bytes.arrayZ, expected, sizeof (expected)));
bytes.fini ();
}
static void
test_medium_rule ()
{
static const char data[] = {
0, 2, 0, 1, /* inputCount, lookupCount */
0, 0, 5, /* input glyph */
0, 1, 0, 2, /* LookupRecord */
};
static const char expected[] = {
0, 2, 0, 1, /* inputCount, lookupCount */
1, 0, 1, /* input glyph */
0, 1, 0, 3, /* LookupRecord */
};
const auto &rule = *reinterpret_cast<const OT::Rule<OT::Layout::MediumTypes> *> (data);
hb_map_t glyph_map;
glyph_map.set (5, 0x10001);
hb_map_t lookup_map;
lookup_map.set (2, 3);
char buf[32];
hb_serialize_context_t s (buf, sizeof (buf));
s.start_serialize ();
hb_always_assert (rule.serialize (&s, &glyph_map, &lookup_map));
s.end_serialize ();
hb_bytes_t bytes = s.copy_bytes ();
hb_always_assert (bytes.length == sizeof (expected));
hb_always_assert (!hb_memcmp (bytes.arrayZ, expected, sizeof (expected)));
bytes.fini ();
}
static void
test_medium_chain_rule ()
{
static const char data[] = {
0, 1, 0, 0, 5, /* backtrack */
0, 2, 0, 0, 6, /* input */
0, 1, 0, 0, 7, /* lookahead */
0, 1, 0, 0, 0, 2, /* LookupRecord */
};
static const char expected[] = {
0, 1, 1, 0, 1, /* backtrack */
0, 2, 1, 0, 2, /* input */
0, 1, 1, 0, 3, /* lookahead */
0, 1, 0, 0, 0, 3, /* LookupRecord */
};
const auto &rule = *reinterpret_cast<const OT::ChainRule<OT::Layout::MediumTypes> *> (data);
hb_map_t glyph_map;
glyph_map.set (5, 0x10001);
glyph_map.set (6, 0x10002);
glyph_map.set (7, 0x10003);
hb_map_t lookup_map;
lookup_map.set (2, 3);
char buf[32];
hb_serialize_context_t s (buf, sizeof (buf));
s.start_serialize ();
hb_always_assert (rule.serialize (&s, &lookup_map, &glyph_map));
s.end_serialize ();
hb_bytes_t bytes = s.copy_bytes ();
hb_always_assert (bytes.length == sizeof (expected));
hb_always_assert (!hb_memcmp (bytes.arrayZ, expected, sizeof (expected)));
bytes.fini ();
}
static void
test_device_only_value ()
{
using namespace OT::Layout::GPOS_impl;
static const char data[] = {
0, 2, /* Device offset */
0, 0, 0, 0, (char) 0x80, 0, /* VariationIndex 0 */
};
const auto *base = reinterpret_cast<const ValueBase *> (data);
const auto *values = reinterpret_cast<const Value *> (data);
for (unsigned device = ValueFormat::xPlaDevice;
device <= ValueFormat::yAdvDevice; device <<= 1)
for (bool partial : {false, true})
{
ValueFormat format;
format = device;
hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> mapping;
mapping.set (0, hb_pair (partial ? 1u : OT::VarIdx::NO_VARIATION, 25));
unsigned new_format = format.get_effective_format (values, false, false, base, &mapping);
hb_always_assert (new_format == (device >> 4 | (partial ? device : 0)));
char buf[32];
hb_serialize_context_t s (buf, sizeof (buf));
s.start_serialize ();
format.copy_values (&s, new_format, base, values, &mapping);
s.end_serialize ();
hb_bytes_t bytes = s.copy_bytes ();
hb_always_assert (!s.in_error ());
hb_always_assert (bytes.length >= OT::HBINT16::static_size);
hb_always_assert (*reinterpret_cast<const OT::HBINT16 *> (bytes.arrayZ) == 25);
bytes.fini ();
}
}
static void
test_failed_class_mapping ()
{
hb_set_t klasses;
klasses.add (1);
hb_sorted_vector_t<hb_codepoint_pair_t> glyph_and_klass;
glyph_and_klass.push (hb_pair (hb_codepoint_t (42), hb_codepoint_t (1)));
hb_map_t klass_map;
klass_map.successful = false; /* Simulate failure allocating the class map. */
char buf[32];
hb_serialize_context_t s (buf, sizeof (buf));
s.start_serialize<OT::ClassDef> ();
hb_always_assert (!OT::ClassDef_remap_and_serialize (
&s, klasses, true, glyph_and_klass, &klass_map));
hb_always_assert (s.in_error () && !s.only_overflow ());
hb_always_assert (glyph_and_klass[0].second == 1);
}
int
main (int argc, char **argv)
{
test_null_condition ();
test_medium_rule ();
test_medium_chain_rule ();
test_device_only_value ();
test_failed_class_mapping ();
char buf[16384];
hb_serialize_context_t s (buf, sizeof (buf));
hb_sorted_vector_t<hb_codepoint_t> v{1, 2, 5};
auto c = s.start_serialize<Coverage> ();
c->serialize (&s, hb_iter (v));
s.end_serialize ();
hb_bytes_t bytes = s.copy_bytes ();
hb_always_assert (bytes.length == 10);
bytes.fini ();
return 0;
}