Skip to content

Commit

Permalink
cff: Fix two issues when subsetting CFF2
Browse files Browse the repository at this point in the history
1. Hints were not being included in the subset font which was throwing
   things off when a hint operator was encountered by a PDF renderer.
2. The subsetter was determining whether to generate a Type 1 or
   CID-keyed font automatically but this was not expected by Prince,
   which expected a CID font out if a CID font went in (which is the
   case for all CFF2 fonts). This was addressed by adding a parameter to
   influence the output format.
  • Loading branch information
wezm committed Apr 30, 2024
1 parent 0ea2da2 commit 560129f
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 9 deletions.
17 changes: 14 additions & 3 deletions src/cff/cff2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,13 @@ pub(crate) enum StackValue {
Fixed(Fixed),
}

// The CFF format to output when subsetting CFF2 to CFF.
#[derive(Debug, Copy, Clone)]
pub enum OutputFormat {
Type1OrCid,
CidOnly,
}

impl<'a> CFF2<'a> {
/// Create a non-variable instance of a variable CFF2 font according to `instance`.
pub fn instance_char_strings(&mut self, instance: &OwnedTuple) -> Result<(), VariationError> {
Expand Down Expand Up @@ -188,11 +195,12 @@ impl<'a> CFF2<'a> {
/// Create a subset of this CFF2 font.
///
/// `glpyh_ids` contains the ids of the glyphs to retain. It must begin with 0 (`.notdef`).
pub fn subset(
pub fn subset_to_cff(
&'a self,
glyph_ids: &[u16],
table_provider: &impl FontTableProvider,
include_fstype: bool,
output_format: OutputFormat,
) -> Result<SubsetCFF<'a>, SubsetError> {
if glyph_ids.len() > usize::from(u16::MAX) {
return Err(SubsetError::TooManyGlyphs);
Expand All @@ -213,7 +221,10 @@ impl<'a> CFF2<'a> {
// > If generating CFF 1-compatible font instance from a CFF2 variable font that has more
// > than one Font DICT in the Font DICT INDEX, the CFF 1 font must be written as a
// > CID-keyed font.
let type_1 = glyph_ids.len() < 256 && self.fonts.len() == 1;
let type_1 = match output_format {
OutputFormat::Type1OrCid => glyph_ids.len() < 256 && self.fonts.len() == 1,
OutputFormat::CidOnly => false,
};

// Read tables needed for the conversion
let cmap_data = table_provider.read_table_data(tag::CMAP)?;
Expand Down Expand Up @@ -1475,7 +1486,7 @@ mod tests {
.read::<CFF2<'_>>()
.expect("error parsing CFF2 table");
let subset = cff2
.subset(&[0, 1], &provider, true)
.subset_to_cff(&[0, 1], &provider, true, OutputFormat::Type1OrCid)
.expect("unable to subset CFF2");

// Write it out
Expand Down
6 changes: 5 additions & 1 deletion src/cff/charstring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use super::{cff2, CFFError, CFFFont, CFFVariant, MaybeOwnedIndex, Operator};

mod argstack;

use crate::binary::write::{WriteBinary, WriteBuffer};
use crate::binary::write::{WriteBinary, WriteBuffer, WriteContext};
use crate::cff;
use crate::cff::cff2::BlendOperand;
use crate::cff::charstring::operator::{
Expand Down Expand Up @@ -443,6 +443,10 @@ impl CharStringVisitor<cff2::StackValue, CharStringConversionError> for CharStri
fn exit_subr(&mut self) -> Result<(), CharStringConversionError> {
Ok(U8::write(&mut self.buffer, RETURN)?)
}

fn hint_data(&mut self, _op: VisitOp, hints: &[u8]) -> Result<(), CharStringConversionError> {
Ok(self.buffer.write_bytes(hints)?)
}
}

impl CharStringVisitor<f32, CFFError> for UsedSubrs {
Expand Down
21 changes: 16 additions & 5 deletions src/subset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::binary::read::{ReadArrayCow, ReadScope};
use crate::binary::write::{Placeholder, WriteBinary};
use crate::binary::write::{WriteBinaryDep, WriteBuffer, WriteContext};
use crate::binary::{long_align, U16Be, U32Be};
use crate::cff::cff2::CFF2;
use crate::cff::cff2::{OutputFormat, CFF2};
use crate::cff::{CFFError, SubsetCFF, CFF};
use crate::error::{ParseError, ReadWriteError, WriteError};
use crate::post::PostTable;
Expand Down Expand Up @@ -91,7 +91,13 @@ pub fn subset(
if provider.has_table(tag::CFF) {
subset_cff(provider, glyph_ids, mappings_to_keep, true)
} else if provider.has_table(tag::CFF2) {
subset_cff2(provider, glyph_ids, mappings_to_keep, false)
subset_cff2(
provider,
glyph_ids,
mappings_to_keep,
false,
OutputFormat::Type1OrCid,
)
} else {
subset_ttf(
provider,
Expand Down Expand Up @@ -233,6 +239,7 @@ fn subset_cff2(
glyph_ids: &[u16],
mappings_to_keep: MappingsToKeep<OldIds>,
include_fstype: bool,
output_format: OutputFormat,
) -> Result<Vec<u8>, SubsetError> {
let cff2_data = provider.read_table_data(tag::CFF2)?;
let scope = ReadScope::new(&cff2_data);
Expand All @@ -247,7 +254,9 @@ fn subset_cff2(
))?;

// Build the new CFF table
let cff_subset = cff2.subset(glyph_ids, provider, include_fstype)?.into();
let cff_subset = cff2
.subset_to_cff(glyph_ids, provider, include_fstype, output_format)?
.into();

// Wrap the rest of the OpenType tables around it
build_otf(
Expand Down Expand Up @@ -602,7 +611,7 @@ pub mod prince {
tag, FontTableProvider, MappingsToKeep, ParseError, ReadScope, SubsetError, WriteBinary,
WriteBuffer, CFF,
};
use crate::cff::cff2::CFF2;
use crate::cff::cff2::{OutputFormat, CFF2};
use crate::tables::cmap::subset::{CmapStrategy, CmapTarget};
use std::ffi::c_int;

Expand Down Expand Up @@ -706,7 +715,9 @@ pub mod prince {
let cff2: CFF2<'_> = scope.read::<CFF2<'_>>()?;

// Build the new CFF table
let cff = cff2.subset(glyph_ids, provider, true)?.into();
let cff = cff2
.subset_to_cff(glyph_ids, provider, true, OutputFormat::CidOnly)?
.into();

let mut buffer = WriteBuffer::new();
CFF::write(&mut buffer, &cff)?;
Expand Down

0 comments on commit 560129f

Please sign in to comment.