Something went wrong. Try again.
A Zig library and CLI for reading Mobipocket (.mobi, .prc, .azw) e-books.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263// SPDX-FileCopyrightText: © 2026 Jeffrey C. Ollie <jeff@ocjtech.us>// SPDX-License-Identifier: MIT
//! Text encodings that appear in MOBI files.
const std = @import("std");
pub const Encoding = enum(u32) { cp1252 = 1252, utf8 = 65001, _,};
/// The Unicode code points for CP-1252 bytes 0x80...0x9f. Everything outside/// that window matches Latin-1, where the byte value is the code point.const cp1252_high = [32]u21{ 0x20ac, 0xfffd, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, 0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0xfffd, 0x017d, 0xfffd, 0xfffd, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, 0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0xfffd, 0x017e, 0x0178,};
pub fn cp1252ToUtf8(gpa: std.mem.Allocator, input: []const u8) error{OutOfMemory}![]u8 { var out: std.ArrayList(u8) = .empty; errdefer out.deinit(gpa); try out.ensureTotalCapacity(gpa, input.len); var scratch: [4]u8 = undefined; for (input) |b| { if (b < 0x80) { try out.append(gpa, b); continue; } const cp: u21 = if (b < 0xa0) cp1252_high[b - 0x80] else b; const n = std.unicode.utf8Encode(cp, &scratch) catch unreachable; try out.appendSlice(gpa, scratch[0..n]); } return out.toOwnedSlice(gpa);}
/// Convert `input` from `encoding` to UTF-8. The caller owns the result.pub fn toUtf8(gpa: std.mem.Allocator, encoding: Encoding, input: []const u8) error{OutOfMemory}![]u8 { return switch (encoding) { // Anything that is not CP-1252 is passed through; UTF-8 needs no work and // an unknown encoding is better left untouched than mangled. .cp1252 => cp1252ToUtf8(gpa, input), else => gpa.dupe(u8, input), };}
test "cp1252 high bytes" { const gpa = std.testing.allocator; const out = try cp1252ToUtf8(gpa, &.{ 'a', 0x93, 'b', 0x94, 0xe9 }); defer gpa.free(out); try std.testing.expectEqualStrings("a\u{201c}b\u{201d}\u{e9}", out);}
test "ascii passes through unchanged" { const gpa = std.testing.allocator; const out = try toUtf8(gpa, .cp1252, "plain ascii"); defer gpa.free(out); try std.testing.expectEqualStrings("plain ascii", out);}