Skip to content

Commit

Permalink
feat(documents): add support for pdf to mobi
Browse files Browse the repository at this point in the history
  • Loading branch information
danvergara committed Sep 6, 2024
1 parent 67fcabc commit cbd3398
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 3 deletions.
3 changes: 3 additions & 0 deletions pkg/files/documents/documents.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ const (
EpubMimeType = "epub+zip"
EPUB = "epub"

MOBI = "mobi"
MobiMimeType = "x-mobipocket-ebook"

imageMimeType = "image/"
imageType = "image"

Expand Down
13 changes: 13 additions & 0 deletions pkg/files/documents/documents_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,19 @@ func TestPDFTConvertTo(t *testing.T) {
input input
expected expected
}{
{
name: "pdf to mobi",
input: input{
filename: "testdata/bitcoin.pdf",
mimetype: "application/pdf",
targetFileType: "Ebook",
targetFormat: "mobi",
documenter: documents.NewPdf("bitcoin.pdf"),
},
expected: expected{
mimetype: "application/zip",
},
},
{
name: "pdf to epub",
input: input{
Expand Down
139 changes: 137 additions & 2 deletions pkg/files/documents/pdf.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ func NewPdf(filename string) Pdf {
},
"Ebook": {
EPUB,
MOBI,
},
},
compatibleMIMETypes: map[string][]string{
Expand All @@ -66,10 +67,10 @@ func NewPdf(filename string) Pdf {
},
"Document": {
DOCXMIMEType,
EpubMimeType,
},
"Ebook": {
EpubMimeType,
MobiMimeType,
},
},
}
Expand Down Expand Up @@ -110,6 +111,7 @@ func (p Pdf) ConvertTo(fileType, subType string, file io.Reader) (io.Reader, err
err,
)
}

fileBytes := buf.Bytes()

// If the file type is valid, figures out how to go ahead.
Expand Down Expand Up @@ -514,7 +516,140 @@ func (p Pdf) ConvertTo(fileType, subType string, file io.Reader) (io.Reader, err

if _, err := io.Copy(w1, cf); err != nil {
return nil, fmt.Errorf(
"error at writing the docx file content to the zip writer: %w",
"error at writing the epub file content to the zip writer: %w",
err,
)
}

// Closes both zip writer and the zip file after its done with the writing.
zipWriter.Close()
archive.Close()

// Reads the zip file as an slice of bytes.
zipFile, err := os.ReadFile(archive.Name())
if err != nil {
return nil, fmt.Errorf("error reading zip file: %v", err)
}

return bytes.NewReader(zipFile), nil
case MOBI:
// Create a temporary empty file where the input pdf is gonna be stored.
tmpInputPDF, err := os.Create(
fmt.Sprintf(
"/tmp/%s.%s",
strings.TrimSuffix(p.filename, filepath.Ext(p.filename)),
PDF,
),
)
if err != nil {
return nil, fmt.Errorf("error creating temporary pdf file: %w", err)
}
defer os.Remove(tmpInputPDF.Name())

// Write the content of the input pdf into the temporary file.
if _, err = tmpInputPDF.Write(fileBytes); err != nil {
return nil, fmt.Errorf(
"error writting the input reader to the temporary pdf file",
)
}

if err := tmpInputPDF.Close(); err != nil {
return nil, err
}

mobiName := fmt.Sprintf(
"%s.%s",
strings.TrimSuffix(tmpInputPDF.Name(), filepath.Ext(tmpInputPDF.Name())),
MOBI,
)

cmd := exec.Command("ebook-convert", tmpInputPDF.Name(), mobiName)

// Capture stdout.
stdout, err := cmd.StdoutPipe()
if err != nil {
return nil, err
}

// Capture stderr.
stderr, err := cmd.StderrPipe()
if err != nil {
return nil, err
}

// Start the command.
if err := cmd.Start(); err != nil {
return nil, err
}
// Create readers to read stdout and stderr.
stdoutScanner := bufio.NewScanner(stdout)
stderrScanner := bufio.NewScanner(stderr)

// Read stdout line by line.
go func() {
for stdoutScanner.Scan() {
log.Println("STDOUT:", stdoutScanner.Text())
}
}()

// Read stderr line by line.
go func() {
for stderrScanner.Scan() {
log.Println("STDERR:", stderrScanner.Text())
}
}()

// Wait for the command to finish.
if err := cmd.Wait(); err != nil {
return nil, err
}

// Open the converted file to get the bytes out of it,
// and then turning them into a io.Reader.
cf, err := os.Open(mobiName)
if err != nil {
return nil, err
}
defer os.Remove(cf.Name())

// Parse the file name of the Zip file.
zipFileName := fmt.Sprintf(
"%s.zip",
strings.TrimSuffix(p.filename, filepath.Ext(p.filename)),
)

// Parse the output file name.
outputMobiFilename := fmt.Sprintf(
"%s.%s",
strings.TrimSuffix(p.filename, filepath.Ext(p.filename)),
MOBI,
)

// Creates the zip file that will be returned.
archive, err := os.CreateTemp("", zipFileName)
if err != nil {
return nil, fmt.Errorf(
"error at creating the zip file to store the mobi file: %w",
err,
)
}
defer os.Remove(archive.Name())

// Creates a Zip Writer to add files later on.
zipWriter := zip.NewWriter(archive)

// Adds the image to the zip file.
w1, err := zipWriter.Create(outputMobiFilename)
if err != nil {
return nil, fmt.Errorf(
"error creating the zip writer: %w",
err,
)
}

if _, err := io.Copy(w1, cf); err != nil {
return nil, fmt.Errorf(
"error at writing the epub file content to the zip writer: %w",
err,
)
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/files/ebooks/epub.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func NewEpub(filename string) Epub {
documents.PDF,
},
"Ebook": {
MOBI,
MobiMimeType,
},
},
}
Expand Down

0 comments on commit cbd3398

Please sign in to comment.