/*
* Copyright © 2026 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 .
*/
#ifndef HB_SUBSET_CFF2_TO_CFF1_HH
#define HB_SUBSET_CFF2_TO_CFF1_HH
#include "hb.hh"
#ifndef HB_NO_SUBSET_CFF
#include "hb-ot-cff1-table.hh"
#include "hb-ot-cff2-table.hh"
#include "hb-subset-cff-common.hh"
namespace OT {
// Forward declarations - these are defined in hb-subset-cff2.cc
struct cff2_subset_plan;
}
namespace CFF {
// Forward declaration
struct cff2_top_dict_values_t;
// Default font name for converted CFF1 fonts
static constexpr const char CFF1_DEFAULT_FONT_NAME[] = "CFF1Font" ;
/*
* CFF2 to CFF1 Converter
*
* Converts an instantiated ( pinned ) CFF2 variable font to CFF1 format .
* This is used when instantiating a variable font to a static instance .
*
* IMPLEMENTATION STATUS :
* ✓ CFF1 structure ( Header , Name INDEX , String INDEX , Top DICT INDEX )
* ✓ ROS operator ( makes font CID - keyed : " Adobe - Identity - 0 " )
* ✓ FDArray and FDSelect in Top DICT ( required for CID fonts )
* ✓ FDSelect3 format ( compact range - based , 8 bytes for single - FD fonts )
* ✓ CID Charset with identity mapping ( format 2 )
* ✓ FontBBox from head table ( xMin , yMin , xMax , yMax )
* ✓ Width optimization ( defaultWidthX / nominalWidthX with O ( n ) algorithm )
* ✓ Width encoding in CharStrings ( prepended if ! = defaultWidthX )
* ✓ CharString specialization ( h / v operators , combined when possible )
* ✓ Stack depth control ( generalize → specialize with maxstack = 48 )
* ✓ CharStrings with endchar operators ( CFF1 requires , CFF2 doesn ' t )
* ✓ Private DICT instantiation ( blend operators evaluated )
* ✓ Desubroutinized path ( CharStrings are flattened , no subroutines )
* ✓ OTS validation passes
* ✓ HarfBuzz rendering works
*
* FUTURE ENHANCEMENTS :
* - Curve operator specialization ( hhcurveto , vvcurveto , etc . )
* - Peephole optimization ( minor additional size savings )
*
* Key conversions :
* - Version : 2 - > 1
* - Add Name INDEX ( required in CFF1 )
* - Wrap Top DICT in an INDEX ( inline in CFF2 , indexed in CFF1 )
* - Add String INDEX ( " Adobe " , " Identity " for ROS operator )
* - Add ROS operator to Top DICT ( makes it CID - keyed )
* - Add FDSelect to Top DICT ( required in CFF1 even with single FD )
* - Add endchar to CharStrings ( required in CFF1 , optional in CFF2 )
*/
struct cff1_subset_plan_from_cff2_t
{
// Inherits most data from cff2_subset_plan
const OT::cff2_subset_plan *cff2_plan;
// CFF1-specific additions
hb_vector_t<unsigned char > fontName; // Single font name for Name INDEX
bool create (const OT::cff2_subset_plan &cff2_plan_)
{
cff2_plan = &cff2_plan_;
// Create a simple font name (CFF1 requires a Name INDEX)
fontName.resize (strlen (CFF1_DEFAULT_FONT_NAME));
if (fontName.in_error ()) return false ;
memcpy (fontName.arrayZ, CFF1_DEFAULT_FONT_NAME, strlen (CFF1_DEFAULT_FONT_NAME));
return true ;
}
};
/* CFF1 Top DICT operator serializer that adds ROS and removes CFF2-specific ops */
struct cff1_from_cff2_top_dict_op_serializer_t : cff_top_dict_op_serializer_t<>
{
bool serialize (hb_serialize_context_t *c,
const op_str_t &opstr,
const cff_sub_table_info_t &info) const
{
TRACE_SERIALIZE (this );
switch (opstr.op)
{
case OpCode_vstore:
// CFF2-only operator, skip it
return_trace (true );
case OpCode_CharStrings:
return_trace (FontDict::serialize_link4_op(c, opstr.op, info.char_strings_link, whence_t::Absolute));
case OpCode_FDArray:
case OpCode_FDSelect:
// These are explicitly serialized in the main function to ensure they're present
// even if CFF2 doesn't have them. Skip them here to avoid duplication.
return_trace (true );
default :
return_trace (copy_opstr (c, opstr));
}
}
// Serialize ROS operator to make this a CID-keyed font
bool serialize_ros (hb_serialize_context_t *c) const
{
TRACE_SERIALIZE (this );
// ROS = Registry-Ordering-Supplement
// We use "Adobe", "Identity", 0 for maximum compatibility
// Allocate space and encode directly
// Registry: SID for "Adobe" (custom string at index 0 = SID 391)
// Ordering: SID for "Identity" (custom string at index 1 = SID 392)
// Supplement: 0
// Note: CFF standard strings end at SID 390, custom strings start at 391
str_buff_t buff;
str_encoder_t encoder (buff);
encoder.encode_int (391 ); // Registry SID ("Adobe" in our String INDEX)
encoder.encode_int (392 ); // Ordering SID ("Identity" in our String INDEX)
encoder.encode_int (0 ); // Supplement
encoder.encode_op (OpCode_ROS);
if (encoder.in_error ())
return_trace (false );
auto bytes = buff.as_bytes ();
return_trace (c->embed (bytes.arrayZ, bytes.length));
}
};
/* Main serialization function */
HB_INTERNAL bool
serialize_cff2_to_cff1 (hb_serialize_context_t *c,
OT::cff2_subset_plan &plan,
const cff2_top_dict_values_t &cff2_topDict,
const OT::cff2::accelerator_subset_t &acc);
} /* namespace CFF */
#endif /* HB_NO_SUBSET_CFF */
#endif /* HB_SUBSET_CFF2_TO_CFF1_HH */
Messung V0.5 in Prozent C=89 H=86 G=87
¤ Dauer der Verarbeitung: 0.22 Sekunden
(vorverarbeitet am 2026-09-28)
¤
*© Formatika GbR, Deutschland