Coverage Report

Created: 2026-03-31 11:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/work/workdir/UnpackedTarball/harfbuzz/src/hb-subset-cff2-to-cff1.hh
Line
Count
Source
1
/*
2
 * Copyright © 2026 Behdad Esfahbod
3
 *
4
 *  This is part of HarfBuzz, a text shaping library.
5
 *
6
 * Permission is hereby granted, without written agreement and without
7
 * license or royalty fees, to use, copy, modify, and distribute this
8
 * software and its documentation for any purpose, provided that the
9
 * above copyright notice and the following two paragraphs appear in
10
 * all copies of this software.
11
 *
12
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16
 * DAMAGE.
17
 *
18
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
21
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23
 */
24
25
#ifndef HB_SUBSET_CFF2_TO_CFF1_HH
26
#define HB_SUBSET_CFF2_TO_CFF1_HH
27
28
#include "hb.hh"
29
30
#ifndef HB_NO_SUBSET_CFF
31
32
#include "hb-ot-cff1-table.hh"
33
#include "hb-ot-cff2-table.hh"
34
#include "hb-subset-cff-common.hh"
35
36
namespace OT {
37
  // Forward declarations - these are defined in hb-subset-cff2.cc
38
  struct cff2_subset_plan;
39
}
40
41
namespace CFF {
42
43
// Forward declaration
44
struct cff2_top_dict_values_t;
45
46
// Default font name for converted CFF1 fonts
47
static constexpr const char CFF1_DEFAULT_FONT_NAME[] = "CFF1Font";
48
49
/*
50
 * CFF2 to CFF1 Converter
51
 *
52
 * Converts an instantiated (pinned) CFF2 variable font to CFF1 format.
53
 * This is used when instantiating a variable font to a static instance.
54
 *
55
 * IMPLEMENTATION STATUS:
56
 * ✓ CFF1 structure (Header, Name INDEX, String INDEX, Top DICT INDEX)
57
 * ✓ ROS operator (makes font CID-keyed: "Adobe-Identity-0")
58
 * ✓ FDArray and FDSelect in Top DICT (required for CID fonts)
59
 * ✓ FDSelect3 format (compact range-based, 8 bytes for single-FD fonts)
60
 * ✓ CID Charset with identity mapping (format 2)
61
 * ✓ FontBBox from head table (xMin, yMin, xMax, yMax)
62
 * ✓ Width optimization (defaultWidthX/nominalWidthX with O(n) algorithm)
63
 * ✓ Width encoding in CharStrings (prepended if != defaultWidthX)
64
 * ✓ CharString specialization (h/v operators, combined when possible)
65
 * ✓ Stack depth control (generalize→specialize with maxstack=48)
66
 * ✓ CharStrings with endchar operators (CFF1 requires, CFF2 doesn't)
67
 * ✓ Private DICT instantiation (blend operators evaluated)
68
 * ✓ Desubroutinized path (CharStrings are flattened, no subroutines)
69
 * ✓ OTS validation passes
70
 * ✓ HarfBuzz rendering works
71
 *
72
 * FUTURE ENHANCEMENTS:
73
 * - Curve operator specialization (hhcurveto, vvcurveto, etc.)
74
 * - Peephole optimization (minor additional size savings)
75
 *
76
 * Key conversions:
77
 * - Version: 2 -> 1
78
 * - Add Name INDEX (required in CFF1)
79
 * - Wrap Top DICT in an INDEX (inline in CFF2, indexed in CFF1)
80
 * - Add String INDEX ("Adobe", "Identity" for ROS operator)
81
 * - Add ROS operator to Top DICT (makes it CID-keyed)
82
 * - Add FDSelect to Top DICT (required in CFF1 even with single FD)
83
 * - Add endchar to CharStrings (required in CFF1, optional in CFF2)
84
 */
85
86
struct cff1_subset_plan_from_cff2_t
87
{
88
  // Inherits most data from cff2_subset_plan
89
  const OT::cff2_subset_plan *cff2_plan;
90
91
  // CFF1-specific additions
92
  hb_vector_t<unsigned char> fontName;  // Single font name for Name INDEX
93
94
  bool create (const OT::cff2_subset_plan &cff2_plan_)
95
0
  {
96
0
    cff2_plan = &cff2_plan_;
97
0
98
0
    // Create a simple font name (CFF1 requires a Name INDEX)
99
0
    fontName.resize (strlen (CFF1_DEFAULT_FONT_NAME));
100
0
    if (fontName.in_error ()) return false;
101
0
    memcpy (fontName.arrayZ, CFF1_DEFAULT_FONT_NAME, strlen (CFF1_DEFAULT_FONT_NAME));
102
0
103
0
    return true;
104
0
  }
105
};
106
107
/* CFF1 Top DICT operator serializer that adds ROS and removes CFF2-specific ops */
108
struct cff1_from_cff2_top_dict_op_serializer_t : cff_top_dict_op_serializer_t<>
109
{
110
  bool serialize (hb_serialize_context_t *c,
111
                  const op_str_t &opstr,
112
                  const cff_sub_table_info_t &info) const
113
0
  {
114
0
    TRACE_SERIALIZE (this);
115
116
0
    switch (opstr.op)
117
0
    {
118
0
      case OpCode_vstore:
119
        // CFF2-only operator, skip it
120
0
        return_trace (true);
121
122
0
      case OpCode_CharStrings:
123
0
        return_trace (FontDict::serialize_link4_op(c, opstr.op, info.char_strings_link, whence_t::Absolute));
124
125
0
      case OpCode_FDArray:
126
0
      case OpCode_FDSelect:
127
        // These are explicitly serialized in the main function to ensure they're present
128
        // even if CFF2 doesn't have them. Skip them here to avoid duplication.
129
0
        return_trace (true);
130
131
0
      default:
132
0
        return_trace (copy_opstr (c, opstr));
133
0
    }
134
0
  }
135
136
  // Serialize ROS operator to make this a CID-keyed font
137
  bool serialize_ros (hb_serialize_context_t *c) const
138
0
  {
139
0
    TRACE_SERIALIZE (this);
140
141
    // ROS = Registry-Ordering-Supplement
142
    // We use "Adobe", "Identity", 0 for maximum compatibility
143
144
    // Allocate space and encode directly
145
    // Registry: SID for "Adobe" (custom string at index 0 = SID 391)
146
    // Ordering: SID for "Identity" (custom string at index 1 = SID 392)
147
    // Supplement: 0
148
    // Note: CFF standard strings end at SID 390, custom strings start at 391
149
150
0
    str_buff_t buff;
151
0
    str_encoder_t encoder (buff);
152
153
0
    encoder.encode_int (391);  // Registry SID ("Adobe" in our String INDEX)
154
0
    encoder.encode_int (392);  // Ordering SID ("Identity" in our String INDEX)
155
0
    encoder.encode_int (0);    // Supplement
156
0
    encoder.encode_op (OpCode_ROS);
157
158
0
    if (encoder.in_error ())
159
0
      return_trace (false);
160
161
0
    auto bytes = buff.as_bytes ();
162
0
    return_trace (c->embed (bytes.arrayZ, bytes.length));
163
0
  }
164
};
165
166
/* Main serialization function */
167
HB_INTERNAL bool
168
serialize_cff2_to_cff1 (hb_serialize_context_t *c,
169
                        OT::cff2_subset_plan &plan,
170
                        const cff2_top_dict_values_t &cff2_topDict,
171
                        const OT::cff2::accelerator_subset_t &acc);
172
173
} /* namespace CFF */
174
175
#endif /* HB_NO_SUBSET_CFF */
176
177
#endif /* HB_SUBSET_CFF2_TO_CFF1_HH */