Analyze branch targets in TrueType instructions
This commit is contained in:
parent
9c07c1a4a6
commit
88d1e3163d
31
src/error.rs
31
src/error.rs
|
@ -125,9 +125,38 @@ pub enum GlyphStoreCreationError {
|
|||
}
|
||||
|
||||
/// An error in hinting instruction evaluation.
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
pub enum HintingError {
|
||||
/// A miscellaneous error occurred.
|
||||
Failed,
|
||||
/// An error was encountered during hinting program analysis.
|
||||
AnalysisError(HintingAnalysisError),
|
||||
}
|
||||
|
||||
/// An error encountered during parsing of the TrueType hinting bytecode.
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
pub enum HintingParseError {
|
||||
/// The instruction stream terminated normally.
|
||||
Eof,
|
||||
/// The instruction stream terminated abnormally.
|
||||
UnexpectedEof,
|
||||
/// An unexpected opcode was encountered.
|
||||
UnknownOpcode,
|
||||
/// An unexpected value was encountered for `DistanceType`.
|
||||
InvalidDistanceType,
|
||||
}
|
||||
|
||||
/// An error encountered during semantic analysis of the TrueType hinting bytecode.
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
pub enum HintingAnalysisError {
|
||||
/// An error occurred while parsing the instruction stream.
|
||||
ParseError(HintingParseError),
|
||||
/// A branch target (e.g. `Eif`) was found without a corresponding branch instruction.
|
||||
BranchTargetMissingBranch,
|
||||
/// A branch target was (e.g. `If`) was found without a corresponding branch target (e.g.
|
||||
/// `Eif`).
|
||||
BranchMissingBranchTarget,
|
||||
/// A branch target was mismatched with its branch instruction (`Eif` vs. `If`, etc.)
|
||||
MismatchedBranchInstruction,
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
//! TrueType instructions.
|
||||
|
||||
use error::HintingParseError;
|
||||
|
||||
/// All TrueType instructions.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Instruction<'a> {
|
||||
|
@ -182,7 +184,7 @@ pub enum Instruction<'a> {
|
|||
/// Else (0x1b) (ttinst2.doc, 315)
|
||||
Else,
|
||||
/// End If (0x59) (ttinst2.doc, 316)
|
||||
EIf,
|
||||
Eif,
|
||||
/// Jump Relative on True (0x78) (ttinst2.doc, 317-318)
|
||||
Jrot,
|
||||
/// Jump (0x1c) (ttinst2.doc, 319)
|
||||
|
@ -256,12 +258,12 @@ pub enum Instruction<'a> {
|
|||
impl<'a> Instruction<'a> {
|
||||
#[inline]
|
||||
pub fn parse<'b, 'c>(data: &'b [u8], pc: &'c mut usize)
|
||||
-> Result<Instruction<'b>, ParseError> {
|
||||
let op = try!(get(data, pc).ok_or(ParseError::Eof));
|
||||
-> Result<Instruction<'b>, HintingParseError> {
|
||||
let op = try!(get(data, pc).ok_or(HintingParseError::Eof));
|
||||
match op {
|
||||
0x40 | 0xb0...0xb7 => {
|
||||
let count = if op == 0x40 {
|
||||
try!(get(data, pc).ok_or(ParseError::UnexpectedEof)) as usize
|
||||
try!(get(data, pc).ok_or(HintingParseError::UnexpectedEof)) as usize
|
||||
} else {
|
||||
(op as usize & 7) + 1
|
||||
};
|
||||
|
@ -270,12 +272,12 @@ impl<'a> Instruction<'a> {
|
|||
*pc += count;
|
||||
Ok(insn)
|
||||
} else {
|
||||
Err(ParseError::UnexpectedEof)
|
||||
Err(HintingParseError::UnexpectedEof)
|
||||
}
|
||||
}
|
||||
0x41 | 0xb8...0xbf => {
|
||||
let count = if op == 0x41 {
|
||||
try!(get(data, pc).ok_or(ParseError::UnexpectedEof)) as usize * 2
|
||||
try!(get(data, pc).ok_or(HintingParseError::UnexpectedEof)) as usize * 2
|
||||
} else {
|
||||
((op as usize & 7) + 1) * 2
|
||||
};
|
||||
|
@ -284,7 +286,7 @@ impl<'a> Instruction<'a> {
|
|||
*pc += count;
|
||||
Ok(insn)
|
||||
} else {
|
||||
Err(ParseError::UnexpectedEof)
|
||||
Err(HintingParseError::UnexpectedEof)
|
||||
}
|
||||
}
|
||||
0x43 => Ok(Instruction::Rs),
|
||||
|
@ -385,7 +387,7 @@ impl<'a> Instruction<'a> {
|
|||
0x8a => Ok(Instruction::Roll),
|
||||
0x58 => Ok(Instruction::If),
|
||||
0x1b => Ok(Instruction::Else),
|
||||
0x59 => Ok(Instruction::EIf),
|
||||
0x59 => Ok(Instruction::Eif),
|
||||
0x78 => Ok(Instruction::Jrot),
|
||||
0x1c => Ok(Instruction::Jmpr),
|
||||
0x79 => Ok(Instruction::Jrof),
|
||||
|
@ -420,7 +422,7 @@ impl<'a> Instruction<'a> {
|
|||
0x4f => Ok(Instruction::Debug),
|
||||
0x88 => Ok(Instruction::Getinfo),
|
||||
0x91 => Ok(Instruction::Getvariation),
|
||||
_ => Err(ParseError::UnknownOpcode),
|
||||
_ => Err(HintingParseError::UnknownOpcode),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -484,25 +486,13 @@ pub enum DistanceType {
|
|||
}
|
||||
|
||||
impl DistanceType {
|
||||
fn parse(value: u8) -> Result<DistanceType, ParseError> {
|
||||
fn parse(value: u8) -> Result<DistanceType, HintingParseError> {
|
||||
match value {
|
||||
0 => Ok(DistanceType::Gray),
|
||||
1 => Ok(DistanceType::Black),
|
||||
2 => Ok(DistanceType::White),
|
||||
_ => Err(ParseError::InvalidDistanceType),
|
||||
_ => Err(HintingParseError::InvalidDistanceType),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
pub enum ParseError {
|
||||
/// The instruction stream terminated normally.
|
||||
Eof,
|
||||
/// The instruction stream terminated abnormally.
|
||||
UnexpectedEof,
|
||||
/// An unexpected opcode was encountered.
|
||||
UnknownOpcode,
|
||||
/// An unexpected value was encountered for `DistanceType`.
|
||||
InvalidDistanceType,
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
// Copyright 2017 The Servo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! The TrueType interpreter.
|
||||
|
||||
use error::{HintingAnalysisError, HintingParseError};
|
||||
use hinting::insns::Instruction;
|
||||
|
||||
pub struct ScriptInterpreter<'a> {
|
||||
bytecode: &'a [u8],
|
||||
branch_targets: Vec<BranchTarget>,
|
||||
}
|
||||
|
||||
impl<'a> ScriptInterpreter<'a> {
|
||||
pub fn new<'b>(bytecode: &'b [u8]) -> Result<ScriptInterpreter<'b>, HintingAnalysisError> {
|
||||
let mut interpreter = ScriptInterpreter {
|
||||
bytecode: bytecode,
|
||||
branch_targets: vec![],
|
||||
};
|
||||
try!(interpreter.populate_branch_targets());
|
||||
println!("{:#?}", interpreter.branch_targets);
|
||||
Ok(interpreter)
|
||||
}
|
||||
|
||||
fn populate_branch_targets(&mut self) -> Result<(), HintingAnalysisError> {
|
||||
let (mut pc, mut pending_branch_targets) = (0, vec![]);
|
||||
loop {
|
||||
let location = pc;
|
||||
let instruction = match Instruction::parse(self.bytecode, &mut pc) {
|
||||
Ok(instruction) => instruction,
|
||||
Err(HintingParseError::Eof) => break,
|
||||
Err(err) => return Err(HintingAnalysisError::ParseError(err)),
|
||||
};
|
||||
|
||||
match instruction {
|
||||
Instruction::If | Instruction::Fdef | Instruction::Idef => {
|
||||
pending_branch_targets.push((location, instruction))
|
||||
}
|
||||
Instruction::Endf => {
|
||||
match pending_branch_targets.pop() {
|
||||
Some((branch_location, Instruction::Fdef)) |
|
||||
Some((branch_location, Instruction::Idef)) => {
|
||||
self.branch_targets.push(BranchTarget {
|
||||
branch_location: branch_location,
|
||||
target_location: location,
|
||||
})
|
||||
}
|
||||
Some(_) => return Err(HintingAnalysisError::MismatchedBranchInstruction),
|
||||
None => return Err(HintingAnalysisError::BranchTargetMissingBranch),
|
||||
}
|
||||
}
|
||||
Instruction::Eif => {
|
||||
match pending_branch_targets.pop() {
|
||||
Some((branch_location, Instruction::If)) |
|
||||
Some((branch_location, Instruction::Else)) => {
|
||||
self.branch_targets.push(BranchTarget {
|
||||
branch_location: branch_location,
|
||||
target_location: location,
|
||||
})
|
||||
}
|
||||
Some(_) => return Err(HintingAnalysisError::MismatchedBranchInstruction),
|
||||
None => return Err(HintingAnalysisError::BranchTargetMissingBranch),
|
||||
}
|
||||
}
|
||||
Instruction::Else => {
|
||||
match pending_branch_targets.pop() {
|
||||
Some((branch_location, Instruction::If)) => {
|
||||
self.branch_targets.push(BranchTarget {
|
||||
branch_location: branch_location,
|
||||
target_location: location,
|
||||
});
|
||||
pending_branch_targets.push((location, instruction))
|
||||
}
|
||||
Some(_) => return Err(HintingAnalysisError::MismatchedBranchInstruction),
|
||||
None => return Err(HintingAnalysisError::BranchTargetMissingBranch),
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if pending_branch_targets.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(HintingAnalysisError::BranchMissingBranchTarget)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct BranchTarget {
|
||||
branch_location: usize,
|
||||
target_location: usize,
|
||||
}
|
||||
|
|
@ -18,8 +18,10 @@ use byteorder::{BigEndian, ByteOrder};
|
|||
use error::HintingError;
|
||||
use euclid::Point2D;
|
||||
use font::Font;
|
||||
use hinting::interp::ScriptInterpreter;
|
||||
|
||||
mod insns;
|
||||
mod interp;
|
||||
|
||||
/// A TrueType hinting virtual machine.
|
||||
pub struct Hinter {
|
||||
|
@ -94,6 +96,8 @@ impl Hinter {
|
|||
graphics_state_flags: AUTO_FLIP,
|
||||
};
|
||||
|
||||
try!(ScriptInterpreter::new(font.font_program()).map_err(HintingError::AnalysisError));
|
||||
|
||||
Ok(hinter)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue