hb-subset.cc 10.2 KB
Newer Older
1
/*
B
Minor  
Behdad Esfahbod 已提交
2
 * Copyright © 2018  Google, Inc.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 *
 *  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.
 *
24
 * Google Author(s): Garret Rieger, Rod Sheeter, Behdad Esfahbod
25 26 27
 */

#include "hb-object-private.hh"
B
Behdad Esfahbod 已提交
28
#include "hb-open-type-private.hh"
29

30
#include "hb-subset-glyf.hh"
31
#include "hb-subset-private.hh"
32
#include "hb-subset-plan.hh"
33

B
Behdad Esfahbod 已提交
34
#include "hb-open-file-private.hh"
35
#include "hb-ot-cmap-table.hh"
36
#include "hb-ot-glyf-table.hh"
37 38
#include "hb-ot-head-table.hh"
#include "hb-ot-maxp-table.hh"
39
#include "hb-ot-os2-table.hh"
40

41

42
#ifndef HB_NO_VISIBILITY
43
const void * const OT::_hb_NullPool[HB_NULL_POOL_SIZE / sizeof (void *)] = {};
44
#endif
45 46


47 48 49 50 51 52 53 54 55 56
struct hb_subset_profile_t {
  hb_object_header_t header;
  ASSERT_POD ();
};

/**
 * hb_subset_profile_create:
 *
 * Return value: New profile with default settings.
 *
57
 * Since: 1.8.0
58 59 60 61 62 63 64 65 66 67
 **/
hb_subset_profile_t *
hb_subset_profile_create ()
{
  return hb_object_create<hb_subset_profile_t>();
}

/**
 * hb_subset_profile_destroy:
 *
68
 * Since: 1.8.0
69 70 71 72 73 74 75 76 77
 **/
void
hb_subset_profile_destroy (hb_subset_profile_t *profile)
{
  if (!hb_object_destroy (profile)) return;

  free (profile);
}

78
template<typename TableType>
79
static hb_blob_t *
R
Rod Sheeter 已提交
80
_subset (hb_subset_plan_t *plan, hb_face_t *source)
81 82
{
    OT::Sanitizer<TableType> sanitizer;
R
Rod Sheeter 已提交
83 84 85
    hb_blob_t *source_blob = sanitizer.sanitize (source->reference_table (TableType::tableTag));
    const TableType *table = OT::Sanitizer<TableType>::lock_instance (source_blob);
    hb_blob_t *result = table->subset(plan, source);
B
Behdad Esfahbod 已提交
86

R
Rod Sheeter 已提交
87
    hb_blob_destroy (source_blob);
R
Rod Sheeter 已提交
88

89
    hb_tag_t tag = TableType::tableTag;
90
    DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c::subset %s", HB_UNTAG(tag), result ? "success" : "FAILED!");
91 92
    return result;
}
93

B
Behdad Esfahbod 已提交
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131

/*
 * A face that has add_table().
 */

struct hb_subset_face_data_t
{
  struct table_entry_t
  {
    inline int cmp (const hb_tag_t *t) const
    {
      if (*t < tag) return -1;
      if (*t > tag) return -1;
      return 0;
    }

    hb_tag_t   tag;
    hb_blob_t *blob;
  };

  hb_prealloced_array_t<table_entry_t, 32> tables;
};

static hb_subset_face_data_t *
_hb_subset_face_data_create (void)
{
  hb_subset_face_data_t *data = (hb_subset_face_data_t *) calloc (1, sizeof (hb_subset_face_data_t));
  if (unlikely (!data))
    return nullptr;

  return data;
}

static void
_hb_subset_face_data_destroy (void *user_data)
{
  hb_subset_face_data_t *data = (hb_subset_face_data_t *) user_data;

B
Behdad Esfahbod 已提交
132 133
  data->tables.finish ();

B
Behdad Esfahbod 已提交
134 135 136 137
  free (data);
}

static hb_blob_t *
B
Behdad Esfahbod 已提交
138
_hb_subset_face_data_reference_blob (hb_subset_face_data_t *data)
B
Behdad Esfahbod 已提交
139 140
{

B
Behdad Esfahbod 已提交
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
  unsigned int table_count = data->tables.len;
  unsigned int face_length = table_count * 16 + 12;

  for (unsigned int i = 0; i < table_count; i++)
    face_length += _hb_ceil_to_4 (hb_blob_get_length (data->tables.array[i].blob));

  char *buf = (char *) malloc (face_length);
  if (unlikely (!buf))
    return nullptr;

  OT::hb_serialize_context_t c (buf, face_length);
  OT::OpenTypeFontFile *f = c.start_serialize<OT::OpenTypeFontFile> ();

  bool is_cff = data->tables.lsearch (HB_TAG ('C','F','F',' ')) || data->tables.lsearch (HB_TAG ('C','F','F','2'));
  hb_tag_t sfnt_tag = is_cff ? OT::OpenTypeFontFile::CFFTag : OT::OpenTypeFontFile::TrueTypeTag;

  OT::Supplier<hb_tag_t>    tags_supplier  (&data->tables[0].tag, table_count, sizeof (data->tables[0]));
  OT::Supplier<hb_blob_t *> blobs_supplier (&data->tables[0].blob, table_count, sizeof (data->tables[0]));
  bool ret = f->serialize_single (&c,
				  sfnt_tag,
				  tags_supplier,
				  blobs_supplier,
				  table_count);

  c.end_serialize ();

  if (unlikely (!ret))
B
Behdad Esfahbod 已提交
168
  {
B
Behdad Esfahbod 已提交
169
    free (buf);
B
Behdad Esfahbod 已提交
170 171 172
    return nullptr;
  }

B
Behdad Esfahbod 已提交
173 174 175 176 177 178 179 180 181 182 183 184
  return hb_blob_create (buf, face_length, HB_MEMORY_MODE_WRITABLE, buf, free);
}

static hb_blob_t *
_hb_subset_face_reference_table (hb_face_t *face, hb_tag_t tag, void *user_data)
{
  hb_subset_face_data_t *data = (hb_subset_face_data_t *) user_data;

  if (!tag)
    return _hb_subset_face_data_reference_blob (data);

  hb_subset_face_data_t::table_entry_t *entry = data->tables.lsearch (tag);
B
Behdad Esfahbod 已提交
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
  if (entry)
    return hb_blob_reference (entry->blob);

  return nullptr;
}

static hb_face_t *
hb_subset_face_create (void)
{
  hb_subset_face_data_t *data = _hb_subset_face_data_create ();
  if (unlikely (!data)) return hb_face_get_empty ();

  return hb_face_create_for_tables (_hb_subset_face_reference_table,
				    data,
				    _hb_subset_face_data_destroy);
}

static bool
hb_subset_face_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob)
{
  if (unlikely (face->destroy != _hb_subset_face_data_destroy))
    return false;

  hb_subset_face_data_t *data = (hb_subset_face_data_t *) face->user_data;
B
Behdad Esfahbod 已提交
209
  hb_subset_face_data_t::table_entry_t *entry = data->tables.push ();
B
Behdad Esfahbod 已提交
210 211 212 213 214 215 216 217 218
  if (unlikely (!entry))
    return false;

  entry->tag = tag;
  entry->blob = hb_blob_reference (blob);

  return true;
}

B
Behdad Esfahbod 已提交
219
static bool
220 221 222 223
_add_head_and_set_loca_version (hb_face_t *source, bool use_short_loca, hb_face_t *dest)
{
  hb_blob_t *head_blob = OT::Sanitizer<OT::head>().sanitize (hb_face_reference_table (source, HB_OT_TAG_head));
  const OT::head *head = OT::Sanitizer<OT::head>::lock_instance (head_blob);
224
  bool has_head = (head != nullptr);
225

226
  if (has_head) {
227 228 229 230 231 232
    OT::head *head_prime = (OT::head *) calloc (OT::head::static_size, 1);
    memcpy (head_prime, head, OT::head::static_size);
    head_prime->indexToLocFormat.set (use_short_loca ? 0 : 1);

    hb_blob_t *head_prime_blob = hb_blob_create ((const char*) head_prime,
                                                 OT::head::static_size,
233
                                                 HB_MEMORY_MODE_READONLY,
234 235
                                                 head_prime,
                                                 free);
236
    has_head = hb_subset_face_add_table (dest, HB_OT_TAG_head, head_prime_blob);
237 238 239 240
    hb_blob_destroy (head_prime_blob);
  }

  hb_blob_destroy (head_blob);
241
  return has_head;
242 243
}

B
Behdad Esfahbod 已提交
244
static bool
245 246 247 248 249 250
_subset_glyf (hb_subset_plan_t *plan, hb_face_t *source, hb_face_t *dest)
{
  hb_blob_t *glyf_prime = nullptr;
  hb_blob_t *loca_prime = nullptr;

  bool success = true;
251
  bool use_short_loca = false;
252
  // TODO(grieger): Migrate to subset function on the table like cmap.
253
  if (hb_subset_glyf_and_loca (plan, source, &use_short_loca, &glyf_prime, &loca_prime)) {
254 255
    success = success && hb_subset_face_add_table (dest, HB_OT_TAG_glyf, glyf_prime);
    success = success && hb_subset_face_add_table (dest, HB_OT_TAG_loca, loca_prime);
256
    success = success && _add_head_and_set_loca_version (source, use_short_loca, dest);
257 258 259 260 261 262 263 264 265
  } else {
    success = false;
  }
  hb_blob_destroy (loca_prime);
  hb_blob_destroy (glyf_prime);

  return success;
}

B
Behdad Esfahbod 已提交
266
static bool
267 268 269
_subset_table (hb_subset_plan_t *plan,
               hb_face_t        *source,
               hb_tag_t          tag,
R
Rod Sheeter 已提交
270
               hb_blob_t        *source_blob,
271 272 273
               hb_face_t        *dest)
{
  // TODO (grieger): Handle updating the head table (loca format + num glyphs)
R
Rod Sheeter 已提交
274 275
  DEBUG_MSG(SUBSET, nullptr, "begin subset %c%c%c%c", HB_UNTAG(tag));
  hb_blob_t *dest_blob;
276 277 278
  switch (tag) {
    case HB_OT_TAG_glyf:
      return _subset_glyf (plan, source, dest);
279 280 281
    case HB_OT_TAG_head:
      // SKIP head, it's handled by glyf
      return true;
282
    case HB_OT_TAG_maxp:
283 284
      dest_blob = _subset<const OT::maxp> (plan, source);
      break;
285
    case HB_OT_TAG_loca:
286
      // SKIP loca, it's handle by glyf
287 288
      return true;
    case HB_OT_TAG_cmap:
R
Rod Sheeter 已提交
289 290
      dest_blob = _subset<const OT::cmap> (plan, source);
      break;
291 292 293
    case HB_OT_TAG_os2:
      dest_blob = _subset<const OT::os2> (plan, source);
      break;
294
    default:
R
Rod Sheeter 已提交
295 296
      dest_blob = source_blob;
      break;
297
  }
R
Rod Sheeter 已提交
298 299 300 301
  DEBUG_MSG(SUBSET, nullptr, "subset %c%c%c%c %s", HB_UNTAG(tag), dest_blob ? "ok" : "FAILED");
  if (unlikely(!dest_blob)) return false;
  if (unlikely(!hb_subset_face_add_table (dest, tag, dest_blob))) return false;
  return true;
302 303
}

R
Rod Sheeter 已提交
304
static bool
305
_should_drop_table(hb_tag_t tag)
R
Rod Sheeter 已提交
306 307
{
    switch (tag) {
308 309 310
      case HB_TAG ('G', 'D', 'E', 'F'): /* temporary */
      case HB_TAG ('G', 'P', 'O', 'S'): /* temporary */
      case HB_TAG ('G', 'S', 'U', 'B'): /* temporary */
G
Garret Rieger 已提交
311
      case HB_TAG ('D', 'S', 'I', 'G'):
R
Rod Sheeter 已提交
312 313 314
        return true;
      default:
        return false;
315
  }
R
Rod Sheeter 已提交
316 317
}

318 319
/**
 * hb_subset:
320
 * @source: font face data to be subset.
321 322 323 324 325
 * @profile: profile to use for the subsetting.
 * @input: input to use for the subsetting.
 *
 * Subsets a font according to provided profile and input.
 **/
326 327 328 329
hb_face_t *
hb_subset (hb_face_t *source,
	   hb_subset_profile_t *profile,
           hb_subset_input_t *input)
330
{
331
  if (unlikely (!profile || !input || !source)) return hb_face_get_empty();
332

333
  hb_subset_plan_t *plan = hb_subset_plan_create (source, profile, input);
334

335 336 337
  hb_face_t *dest = hb_subset_face_create ();
  hb_tag_t table_tags[32];
  unsigned int offset = 0, count;
R
Rod Sheeter 已提交
338
  bool success = true;
339 340 341 342 343 344
  do {
    count = ARRAY_LENGTH (table_tags);
    hb_face_get_table_tags (source, offset, &count, table_tags);
    for (unsigned int i = 0; i < count; i++)
    {
      hb_tag_t tag = table_tags[i];
R
Rod Sheeter 已提交
345 346 347 348 349
      if (_should_drop_table(tag))
      {
        DEBUG_MSG(SUBSET, nullptr, "drop %c%c%c%c", HB_UNTAG(tag));
        continue;
      }
350 351 352 353 354
      hb_blob_t *blob = hb_face_reference_table (source, tag);
      success = success && _subset_table (plan, source, tag, blob, dest);
      hb_blob_destroy (blob);
    }
  } while (count == ARRAY_LENGTH (table_tags));
R
Rod Sheeter 已提交
355

356
  hb_subset_plan_destroy (plan);
R
Rod Sheeter 已提交
357
  return success ? dest : hb_face_get_empty ();
358
}