diff --git a/HTMLKit/HTMLTokenizer.m b/HTMLKit/HTMLTokenizer.m index b3c7949..7c40b7c 100644 --- a/HTMLKit/HTMLTokenizer.m +++ b/HTMLKit/HTMLTokenizer.m @@ -128,7 +128,49 @@ - (NSString *)consumeNumberCharacterReference { - return nil; + [_inputStreamReader markCurrentLocation]; + + [_inputStreamReader consumeNextInputCharacter]; + + UTF32Char character = [_inputStreamReader nextInputCharacter]; + unsigned int number; + BOOL success; + + switch (character) { + case LATIN_CAPITAL_LETTER_X: + case LATIN_SMALL_LETTER_X: + [_inputStreamReader consumeNextInputCharacter]; + success = [_inputStreamReader consumeHexInt:&number]; + break; + default: + success = [_inputStreamReader consumeHexInt:&number]; + break; + } + + if (success == NO) { + [_inputStreamReader rewindToMarkedLocation]; + [self emitParseError:@"Invalid characters in numeric entity"]; + return nil; + } + success = [_inputStreamReader consumeCharacter:SEMICOLON]; + if (success == NO) { + [self emitParseError:@"Missing semicolon in numeric entity"]; + } + + unichar numericReplacement = NumericReplacementCharacter(character); + if (numericReplacement != NULL_CHAR) { + [self emitParseError:@"Invalid numeric entity (a defenied replacement exists)"]; + return StringFromUniChar(numericReplacement); + } + if (isValidNumericRange(character)) { + [self emitParseError:@"Invalid numeric entity (invalid Unicode range)"]; + return StringFromUniChar(REPLACEMENT_CHAR); + } + if (isControlOrUndefinedCharacter(character)) { + [self emitParseError:@"Invalid numeric entity (control or undefined character)"]; + } + + return StringFromUTF32Char(character); } #pragma mark - States diff --git a/HTMLKit/HTMLTokenizerCharacters.h b/HTMLKit/HTMLTokenizerCharacters.h index 2e87159..184cd41 100644 --- a/HTMLKit/HTMLTokenizerCharacters.h +++ b/HTMLKit/HTMLTokenizerCharacters.h @@ -97,3 +97,47 @@ NS_INLINE BOOL isValidNumericRange(UTF32Char character) || character > 0x10FFFF); } +NS_INLINE unichar NumericReplacementCharacter(UTF32Char character) +{ + switch(character) { + case 0x00: return 0xFFFD; /* REPLACEMENT CHARACTER */ + case 0x80: return 0x20AC; /* EURO SIGN */ + case 0x82: return 0x201A; /* SINGLE LOW-9 QUOTATION MARK */ + case 0x83: return 0x0192; /* LATIN SMALL LETTER F WITH HOOK */ + case 0x84: return 0x201E; /* DOUBLE LOW-9 QUOTATION MARK */ + case 0x85: return 0x2026; /* HORIZONTAL ELLIPSIS */ + case 0x86: return 0x2020; /* DAGGER */ + case 0x87: return 0x2021; /* DOUBLE DAGGER */ + case 0x88: return 0x02C6; /* MODIFIER LETTER CIRCUMFLEX ACCENT */ + case 0x89: return 0x2030; /* PER MILLE SIGN */ + case 0x8A: return 0x0160; /* LATIN CAPITAL LETTER S WITH CARON */ + case 0x8B: return 0x2039; /* SINGLE LEFT-POINTING ANGLE QUOTATION MARK */ + case 0x8C: return 0x0152; /* LATIN CAPITAL LIGATURE OE */ + case 0x8E: return 0x017D; /* LATIN CAPITAL LETTER Z WITH CARON */ + case 0x91: return 0x2018; /* LEFT SINGLE QUOTATION MARK */ + case 0x92: return 0x2019; /* RIGHT SINGLE QUOTATION MARK */ + case 0x93: return 0x201C; /* LEFT DOUBLE QUOTATION MARK */ + case 0x94: return 0x201D; /* RIGHT DOUBLE QUOTATION MARK */ + case 0x95: return 0x2022; /* BULLET */ + case 0x96: return 0x2013; /* EN DASH */ + case 0x97: return 0x2014; /* EM DASH */ + case 0x98: return 0x02DC; /* SMALL TILDE */ + case 0x99: return 0x2122; /* TRADE MARK SIGN */ + case 0x9A: return 0x0161; /* LATIN SMALL LETTER S WITH CARON */ + case 0x9B: return 0x203A; /* SINGLE RIGHT-POINTING ANGLE QUOTATION MARK */ + case 0x9C: return 0x0153; /* LATIN SMALL LIGATURE OE */ + case 0x9E: return 0x017E; /* LATIN SMALL LETTER Z WITH CARON */ + case 0x9F: return 0x0178; /* LATIN CAPITAL LETTER Y WITH DIAERESIS */ + } + return 0x00; +} + +NS_INLINE NSString * StringFromUniChar(unichar character) +{ + return [[NSString alloc] initWithCharacters:&character length:1]; +} + +NS_INLINE NSString * StringFromUTF32Char(UTF32Char character) +{ + return [[NSString alloc] initWithBytes:&character length:sizeof(character) encoding:NSUTF32StringEncoding]; +} diff --git a/HTMLKit/HTMLTokenizerStates.h b/HTMLKit/HTMLTokenizerStates.h index 8d621b7..752acb4 100644 --- a/HTMLKit/HTMLTokenizerStates.h +++ b/HTMLKit/HTMLTokenizerStates.h @@ -84,7 +84,7 @@ typedef NS_ENUM(NSUInteger, HTMLTokenizerState) #undef STATE_ENTRY }; -static NSString* HTMLTokenizerStatesTable[] = { +static NSString * HTMLTokenizerStatesTable[] = { #define STATE_ENTRY( name, value ) [name] = @#name, TOKENIZER_STATES #undef STATE_ENTRY