| /* |
| * Copyright © 2018 Adobe 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. |
| * |
| * Adobe Author(s): Michiharu Ariza |
| */ |
| |
| #include "hb.hh" |
| |
| #ifndef HB_NO_SUBSET_CFF |
| |
| #include "hb-ot-cff-common.hh" |
| #include "hb-ot-cff2-table.hh" |
| #include "hb-subset-cff-common.hh" |
| |
| /* Disable FDSelect format 0 for compatibility with fonttools which doesn't seem choose it. |
| * Rarely any/much smaller than format 3 anyway. */ |
| #define CFF_SERIALIZE_FDSELECT_0 0 |
| |
| using namespace CFF; |
| |
| |
| /* Determine an optimal FDSelect format according to a provided plan. |
| * |
| * Return value: FDSelect format, size, and ranges for the most compact subset FDSelect |
| * along with a font index remapping table |
| */ |
| |
| bool |
| hb_plan_subset_cff_fdselect (const hb_subset_plan_t *plan, |
| unsigned int fdCount, |
| const FDSelect &src, /* IN */ |
| unsigned int &subset_fd_count /* OUT */, |
| unsigned int &subset_fdselect_size /* OUT */, |
| unsigned int &subset_fdselect_format /* OUT */, |
| hb_vector_t<code_pair_t> &fdselect_ranges /* OUT */, |
| hb_inc_bimap_t &fdmap /* OUT */) |
| { |
| subset_fd_count = 0; |
| subset_fdselect_size = 0; |
| subset_fdselect_format = 0; |
| unsigned int num_ranges = 0; |
| |
| unsigned int subset_num_glyphs = plan->num_output_glyphs (); |
| if (subset_num_glyphs == 0) |
| return true; |
| |
| { |
| /* use hb_set to determine the subset of font dicts */ |
| hb_set_t set; |
| hb_codepoint_t prev_fd = CFF_UNDEF_CODE; |
| hb_pair_t<unsigned, hb_codepoint_t> last_range {0, 0}; |
| auto it = hb_iter (plan->new_to_old_gid_list); |
| auto _ = *it; |
| for (hb_codepoint_t gid = 0; gid < subset_num_glyphs; gid++) |
| { |
| hb_codepoint_t old_glyph; |
| if (gid == _.first) |
| { |
| old_glyph = _.second; |
| _ = *++it; |
| } |
| else |
| { |
| /* fonttools retains FDSelect & font dicts for missing glyphs. do the same */ |
| old_glyph = gid; |
| } |
| if (old_glyph >= last_range.second) |
| last_range = src.get_fd_range (old_glyph); |
| unsigned fd = last_range.first; |
| |
| if (fd != prev_fd) |
| { |
| set.add (fd); |
| num_ranges++; |
| prev_fd = fd; |
| fdselect_ranges.push (code_pair_t { fd, gid }); |
| |
| if (gid == old_glyph) |
| gid = hb_min (_.first - 1, last_range.second - 1); |
| } |
| } |
| |
| subset_fd_count = set.get_population (); |
| if (subset_fd_count == fdCount) |
| { |
| /* all font dicts belong to the subset. no need to subset FDSelect & FDArray */ |
| fdmap.identity (fdCount); |
| } |
| else |
| { |
| /* create a fdmap */ |
| fdmap.reset (); |
| |
| hb_codepoint_t fd = CFF_UNDEF_CODE; |
| while (set.next (&fd)) |
| fdmap.add (fd); |
| if (unlikely (fdmap.get_population () != subset_fd_count)) |
| return false; |
| } |
| |
| /* update each font dict index stored as "code" in fdselect_ranges */ |
| for (unsigned int i = 0; i < fdselect_ranges.length; i++) |
| fdselect_ranges[i].code = fdmap[fdselect_ranges[i].code]; |
| } |
| |
| /* determine which FDSelect format is most compact */ |
| if (subset_fd_count > 0xFF) |
| { |
| if (unlikely (src.format != 4)) |
| return false; |
| subset_fdselect_format = 4; |
| subset_fdselect_size = FDSelect::min_size + FDSelect4::min_size + FDSelect4_Range::static_size * num_ranges + HBUINT32::static_size; |
| } |
| else |
| { |
| #if CFF_SERIALIZE_FDSELECT_0 |
| unsigned int format0_size = FDSelect::min_size + FDSelect0::min_size + HBUINT8::static_size * subset_num_glyphs; |
| #endif |
| unsigned int format3_size = FDSelect::min_size + FDSelect3::min_size + FDSelect3_Range::static_size * num_ranges + HBUINT16::static_size; |
| |
| #if CFF_SERIALIZE_FDSELECT_0 |
| if (format0_size <= format3_size) |
| { |
| // subset_fdselect_format = 0; |
| subset_fdselect_size = format0_size; |
| } |
| else |
| #endif |
| { |
| subset_fdselect_format = 3; |
| subset_fdselect_size = format3_size; |
| } |
| } |
| |
| return true; |
| } |
| |
| template <typename FDSELECT3_4> |
| static inline bool |
| serialize_fdselect_3_4 (hb_serialize_context_t *c, |
| const unsigned int num_glyphs, |
| const FDSelect &src, |
| unsigned int size, |
| const hb_vector_t<code_pair_t> &fdselect_ranges) |
| { |
| TRACE_SERIALIZE (this); |
| FDSELECT3_4 *p = c->allocate_size<FDSELECT3_4> (size); |
| if (unlikely (!p)) return_trace (false); |
| p->nRanges () = fdselect_ranges.length; |
| for (unsigned int i = 0; i < fdselect_ranges.length; i++) |
| { |
| p->ranges[i].first = fdselect_ranges[i].glyph; |
| p->ranges[i].fd = fdselect_ranges[i].code; |
| } |
| p->sentinel () = num_glyphs; |
| return_trace (true); |
| } |
| |
| /* Serialize a subset FDSelect format planned above. */ |
| bool |
| hb_serialize_cff_fdselect (hb_serialize_context_t *c, |
| const unsigned int num_glyphs, |
| const FDSelect &src, |
| unsigned int fd_count, |
| unsigned int fdselect_format, |
| unsigned int size, |
| const hb_vector_t<code_pair_t> &fdselect_ranges) |
| { |
| TRACE_SERIALIZE (this); |
| FDSelect *p = c->allocate_min<FDSelect> (); |
| if (unlikely (!p)) return_trace (false); |
| p->format = fdselect_format; |
| size -= FDSelect::min_size; |
| |
| switch (fdselect_format) |
| { |
| #if CFF_SERIALIZE_FDSELECT_0 |
| case 0: |
| { |
| FDSelect0 *p = c->allocate_size<FDSelect0> (size); |
| if (unlikely (!p)) return_trace (false); |
| unsigned int range_index = 0; |
| unsigned int fd = fdselect_ranges[range_index++].code; |
| for (unsigned int i = 0; i < num_glyphs; i++) |
| { |
| if ((range_index < fdselect_ranges.len) && |
| (i >= fdselect_ranges[range_index].glyph)) |
| { |
| fd = fdselect_ranges[range_index++].code; |
| } |
| p->fds[i] = fd; |
| } |
| return_trace (true); |
| } |
| #endif /* CFF_SERIALIZE_FDSELECT_0 */ |
| |
| case 3: |
| return serialize_fdselect_3_4<FDSelect3> (c, num_glyphs, src, |
| size, fdselect_ranges); |
| |
| case 4: |
| return serialize_fdselect_3_4<FDSelect4> (c, num_glyphs, src, |
| size, fdselect_ranges); |
| |
| default: |
| return_trace (false); |
| } |
| } |
| |
| |
| #endif |