Skip to content

Commit

Permalink
improve mobi
Browse files Browse the repository at this point in the history
  • Loading branch information
ewilan-riviere committed Sep 20, 2023
1 parent a2bb07c commit 329a291
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 34 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ There is a lot of different formats for eBooks and comics, if you want to know m
| :--------------: | :-------------------------------------------------------------: | :-------: | :-----------: |
| EPUB (IDPF) | `.epub` || |
| Kindle (Amazon) | `.azw`, `.azw3`, `.kf8`, `.kfx` || _proprietary_ |
| Mobipocket (KF8) | `.mobi`, `.prc` || _deprecated_ |
| Mobipocket | `.mobi`, `.prc` || _deprecated_ |
| PDF | `.pdf` || |
| iBook (Apple) | `.ibooks` || _proprietary_ |
| DjVu | `.djvu`, `.djv` || |
Expand Down
1 change: 1 addition & 0 deletions php-mobi
Submodule php-mobi added at e725dd
31 changes: 17 additions & 14 deletions src/Formats/Mobi/Parser/MobiParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@
class MobiParser
{
/**
* @param string[] $errors
* @param PalmRecord[] $palmRecords
* @param ExthRecord[] $exthRecords
*/
protected function __construct(
protected Stream $stream,
protected ?string $error = null,
protected ?array $errors = [],
protected array $palmRecords = [],
protected array $exthRecords = [],
protected ?PalmDOCHeader $palmDOCHeader = null,
Expand All @@ -32,6 +33,10 @@ public static function make(string $path): ?self
$self->parse();
$self->images = MobiImages::make($path);

if (empty($self->errors)) {
$self->errors = null;
}

return $self;
}

Expand All @@ -56,10 +61,7 @@ private function parse(): self
$content = $this->stream->read(8);

if ($content !== 'BOOKMOBI') {
$this->error = 'Invalid file format';
$this->stream->close();

return $this;
$this->errors[] = "File format invalid: {$content} (expected BOOKMOBI)";
}

$this->stream->seek(0);
Expand Down Expand Up @@ -92,9 +94,7 @@ private function parse(): self
$mobiStart = $this->stream->tell();
$header = $this->stream->read(4);
if ($header !== 'MOBI') {
$this->error = 'No MOBI header';

return $this;
$this->errors[] = "Header invalid: {$header} (expected MOBI)";
}

$this->mobiHeader = new MobiHeader(
Expand All @@ -108,9 +108,7 @@ private function parse(): self
$this->stream->seek($mobiStart + $this->mobiHeader->length);
$exthHeader = $this->stream->read(4);
if ($exthHeader !== 'EXTH') {
$this->error = 'No EXTH header';

return $this;
$this->errors[] = "EXTH header invalid: {$exthHeader} (expected EXTH)";
}

$this->exthHeader = new ExthHeader(
Expand All @@ -129,7 +127,9 @@ private function parse(): self
}

$this->exthRecords = $this->exthHeader->records;
$this->isValid = true;
if (empty($this->errors)) {
$this->isValid = true;
}

$this->stream->close();

Expand Down Expand Up @@ -172,9 +172,12 @@ public function getExthRecords(): array
return $this->exthRecords;
}

public function getError(): ?string
/**
* @return string[]|null
*/
public function getErrors(): ?array
{
return $this->error;
return $this->errors;
}

public function getImages(): ?MobiImages
Expand Down
3 changes: 3 additions & 0 deletions src/Formats/Mobi/Parser/MobiReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

namespace Kiwilan\Ebook\Formats\Mobi\Parser;

/**
* @docs https://wiki.mobileread.com/wiki/Mobi
*/
class MobiReader
{
const DRM_SERVER_ID = 1;
Expand Down
28 changes: 28 additions & 0 deletions src/Utils/Stream.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ protected function __construct(
) {
}

/**
* Creates a new instance of Stream.
*
* @throws \Exception
*/
public static function make(string $path): self
{
$resource = fopen($path, 'rb');
Expand All @@ -21,42 +26,65 @@ public static function make(string $path): self
return new self($path, $resource);
}

/**
* Returns the file size in bytes.
*/
public function filesize(): int
{
return filesize($this->path);
}

/**
* Sets the file position indicator for the file pointer.
*/
public function seek(int $offset): int
{
return fseek($this->resource, $offset, SEEK_SET);
}

/**
* Reads a line from the file pointer.
*/
public function read(int $bytes): string|false
{
return fread($this->resource, $bytes);
}

/**
* Reads a 32-bit unsigned integer from the current position of the file read pointer.
*/
public function binaryToDecimal($bytes): int|float
{
return hexdec(bin2hex($bytes));
}

/**
* Returns the current position of the file read/write pointer.
*/
public function tell(): int
{
return ftell($this->resource);
}

/**
* Closes an open file pointer.
*/
public function close(): bool
{
return fclose($this->resource);
}

/**
* Get file path.
*/
public function getPath(): string
{
return $this->path;
}

/**
* Get file resource.
*
* @return resource
*/
public function getResource(): mixed
Expand Down
19 changes: 1 addition & 18 deletions tests/FormatTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
// FORMAT_PDB,
// FORMAT_PDF,
// FORMAT_PMLZ,
// FORMAT_PRC,
FORMAT_PRC,
// FORMAT_RB,
// FORMAT_RTF,
// FORMAT_SNB,
Expand Down Expand Up @@ -76,20 +76,3 @@
expect($ebook->getCover())->toBeInstanceOf(EbookCover::class);
expect('tests/output/mobi/cover.jpg')->toBeReadableFile();
});

it('can parse specific format', function () {
recurseRmdir('tests/output/mobi');
$parser = MobiParser::make(FORMAT_PDB);
ray($parser);

// ray($parser->getImages());
// foreach ($parser->getImages()->getItems() as $key => $value) {
// $path = "tests/output/mobi/{$key}.jpg";
// file_put_contents($path, $value);
// expect($path)->toBeReadableFile();
// }

// $ebook = Ebook::read(FORMAT_MOBI);
// ray($ebook);
// file_put_contents('tests/output/mobi/cover.jpg', $ebook->getCover()?->getContent());
});
2 changes: 1 addition & 1 deletion tests/MobiTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
expect($parser->getExthHeader())->toBeInstanceOf(ExthHeader::class);
expect($parser->getExthRecords())->toBeArray();
expect($parser->getPalmRecords())->toBeArray();
expect($parser->getError())->toBeNull();
expect($parser->getErrors())->toBeNull();

expect($parser->getPalmDOCHeader()->compression)->toBe(2);
expect($parser->getPalmDOCHeader()->textLength)->toBe(231532);
Expand Down

0 comments on commit 329a291

Please sign in to comment.