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.
|
/// An error in hinting instruction evaluation.
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||||
pub enum HintingError {
|
pub enum HintingError {
|
||||||
/// A miscellaneous error occurred.
|
/// A miscellaneous error occurred.
|
||||||
Failed,
|
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.
|
//! TrueType instructions.
|
||||||
|
|
||||||
|
use error::HintingParseError;
|
||||||
|
|
||||||
/// All TrueType instructions.
|
/// All TrueType instructions.
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum Instruction<'a> {
|
pub enum Instruction<'a> {
|
||||||
|
@ -182,7 +184,7 @@ pub enum Instruction<'a> {
|
||||||
/// Else (0x1b) (ttinst2.doc, 315)
|
/// Else (0x1b) (ttinst2.doc, 315)
|
||||||
Else,
|
Else,
|
||||||
/// End If (0x59) (ttinst2.doc, 316)
|
/// End If (0x59) (ttinst2.doc, 316)
|
||||||
EIf,
|
Eif,
|
||||||
/// Jump Relative on True (0x78) (ttinst2.doc, 317-318)
|
/// Jump Relative on True (0x78) (ttinst2.doc, 317-318)
|
||||||
Jrot,
|
Jrot,
|
||||||
/// Jump (0x1c) (ttinst2.doc, 319)
|
/// Jump (0x1c) (ttinst2.doc, 319)
|
||||||
|
@ -256,12 +258,12 @@ pub enum Instruction<'a> {
|
||||||
impl<'a> Instruction<'a> {
|
impl<'a> Instruction<'a> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn parse<'b, 'c>(data: &'b [u8], pc: &'c mut usize)
|
pub fn parse<'b, 'c>(data: &'b [u8], pc: &'c mut usize)
|
||||||
-> Result<Instruction<'b>, ParseError> {
|
-> Result<Instruction<'b>, HintingParseError> {
|
||||||
let op = try!(get(data, pc).ok_or(ParseError::Eof));
|
let op = try!(get(data, pc).ok_or(HintingParseError::Eof));
|
||||||
match op {
|
match op {
|
||||||
0x40 | 0xb0...0xb7 => {
|
0x40 | 0xb0...0xb7 => {
|
||||||
let count = if op == 0x40 {
|
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 {
|
} else {
|
||||||
(op as usize & 7) + 1
|
(op as usize & 7) + 1
|
||||||
};
|
};
|
||||||
|
@ -270,12 +272,12 @@ impl<'a> Instruction<'a> {
|
||||||
*pc += count;
|
*pc += count;
|
||||||
Ok(insn)
|
Ok(insn)
|
||||||
} else {
|
} else {
|
||||||
Err(ParseError::UnexpectedEof)
|
Err(HintingParseError::UnexpectedEof)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0x41 | 0xb8...0xbf => {
|
0x41 | 0xb8...0xbf => {
|
||||||
let count = if op == 0x41 {
|
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 {
|
} else {
|
||||||
((op as usize & 7) + 1) * 2
|
((op as usize & 7) + 1) * 2
|
||||||
};
|
};
|
||||||
|
@ -284,7 +286,7 @@ impl<'a> Instruction<'a> {
|
||||||
*pc += count;
|
*pc += count;
|
||||||
Ok(insn)
|
Ok(insn)
|
||||||
} else {
|
} else {
|
||||||
Err(ParseError::UnexpectedEof)
|
Err(HintingParseError::UnexpectedEof)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0x43 => Ok(Instruction::Rs),
|
0x43 => Ok(Instruction::Rs),
|
||||||
|
@ -385,7 +387,7 @@ impl<'a> Instruction<'a> {
|
||||||
0x8a => Ok(Instruction::Roll),
|
0x8a => Ok(Instruction::Roll),
|
||||||
0x58 => Ok(Instruction::If),
|
0x58 => Ok(Instruction::If),
|
||||||
0x1b => Ok(Instruction::Else),
|
0x1b => Ok(Instruction::Else),
|
||||||
0x59 => Ok(Instruction::EIf),
|
0x59 => Ok(Instruction::Eif),
|
||||||
0x78 => Ok(Instruction::Jrot),
|
0x78 => Ok(Instruction::Jrot),
|
||||||
0x1c => Ok(Instruction::Jmpr),
|
0x1c => Ok(Instruction::Jmpr),
|
||||||
0x79 => Ok(Instruction::Jrof),
|
0x79 => Ok(Instruction::Jrof),
|
||||||
|
@ -420,7 +422,7 @@ impl<'a> Instruction<'a> {
|
||||||
0x4f => Ok(Instruction::Debug),
|
0x4f => Ok(Instruction::Debug),
|
||||||
0x88 => Ok(Instruction::Getinfo),
|
0x88 => Ok(Instruction::Getinfo),
|
||||||
0x91 => Ok(Instruction::Getvariation),
|
0x91 => Ok(Instruction::Getvariation),
|
||||||
_ => Err(ParseError::UnknownOpcode),
|
_ => Err(HintingParseError::UnknownOpcode),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -484,25 +486,13 @@ pub enum DistanceType {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DistanceType {
|
impl DistanceType {
|
||||||
fn parse(value: u8) -> Result<DistanceType, ParseError> {
|
fn parse(value: u8) -> Result<DistanceType, HintingParseError> {
|
||||||
match value {
|
match value {
|
||||||
0 => Ok(DistanceType::Gray),
|
0 => Ok(DistanceType::Gray),
|
||||||
1 => Ok(DistanceType::Black),
|
1 => Ok(DistanceType::Black),
|
||||||
2 => Ok(DistanceType::White),
|
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 error::HintingError;
|
||||||
use euclid::Point2D;
|
use euclid::Point2D;
|
||||||
use font::Font;
|
use font::Font;
|
||||||
|
use hinting::interp::ScriptInterpreter;
|
||||||
|
|
||||||
mod insns;
|
mod insns;
|
||||||
|
mod interp;
|
||||||
|
|
||||||
/// A TrueType hinting virtual machine.
|
/// A TrueType hinting virtual machine.
|
||||||
pub struct Hinter {
|
pub struct Hinter {
|
||||||
|
@ -94,6 +96,8 @@ impl Hinter {
|
||||||
graphics_state_flags: AUTO_FLIP,
|
graphics_state_flags: AUTO_FLIP,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
try!(ScriptInterpreter::new(font.font_program()).map_err(HintingError::AnalysisError));
|
||||||
|
|
||||||
Ok(hinter)
|
Ok(hinter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue