Repository navigation
Speed up PDF and DJVU page turns #14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d01be3f
a4c25fb
7a1eafd
d962983
d00be63
673b29d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Medium When n := strconv.Itoa(page)
+ toolCtx, cancel := context.WithCancel(ctx)
+ defer cancel()
// pdftohtml parses the document again; let it run alongside pdftotext.
linkXML := make(chan []byte, 1)
go func() {
- out, _ := runToolContext(ctx, "pdftohtml", "-xml", "-i", "-q", "-zoom", "1", "-f", n, "-l", n, "-stdout", path)
+ out, _ := runToolContext(toolCtx, "pdftohtml", "-xml", "-i", "-q", "-zoom", "1", "-f", n, "-l", n, "-stdout", path)
linkXML <- out
}()
- out, err := runToolContext(ctx, "pdftotext", "-cropbox", "-bbox-layout", "-f", n, "-l", n, path, "-")
+ out, err := runToolContext(toolCtx, "pdftotext", "-cropbox", "-bbox-layout", "-f", n, "-l", n, path, "-")
if err != nil {
+ cancel()
+ <-linkXML
return nil
}🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in 673b29d. pdfTextLayer now derives a child context and cancels it on return, so an early exit kills pdftohtml instead of letting it run to completion or timeout. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The child context cancels
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Leaving the early returns as they are. The goroutine's only effect is a send into a buffered channel; cancellation kills pdftohtml and runToolLimited's Wait reaps it a few milliseconds later. Nothing reads the result or watches for the goroutine to finish, so blocking the caller on that reap would only delay PrepareText storing the empty layer. On the normal path the receive before pdfLinks already joins the two. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| package book | ||
|
|
||
| import ( | ||
| "bufio" | ||
| "errors" | ||
| "fmt" | ||
| "image" | ||
| "image/color" | ||
| "io" | ||
| ) | ||
|
|
||
| // ppmMIME is the type the page pipeline sees for converter output. Browsers | ||
| // do not display it; PageBytes converts it to PNG. | ||
| const ppmMIME = "image/x-portable-pixmap" | ||
|
|
||
| // pdftoppm and ddjvu write binary PPM, which decodes in one pass. PNG would | ||
| // cost a compression round trip on every page turn. | ||
| func init() { image.RegisterFormat("x-portable-pixmap", "P6", decodePPM, decodePPMConfig) } | ||
|
|
||
| func ppmHeader(r *bufio.Reader) (w, h int, err error) { | ||
| var magic string | ||
| var depth int | ||
| if _, err := fmt.Fscan(r, &magic, &w, &h, &depth); err != nil || magic != "P6" || depth != 255 || w < 1 || h < 1 || w > maxPagePixels/h { | ||
| return 0, 0, errors.New("unsupported PPM page") | ||
| } | ||
| _, err = r.ReadByte() // one whitespace byte precedes the pixels | ||
| return w, h, err | ||
| } | ||
|
|
||
| func decodePPMConfig(r io.Reader) (image.Config, error) { | ||
| w, h, err := ppmHeader(bufio.NewReader(r)) | ||
| return image.Config{ColorModel: color.RGBAModel, Width: w, Height: h}, err | ||
| } | ||
|
|
||
| func decodePPM(r io.Reader) (image.Image, error) { | ||
| br := bufio.NewReader(r) | ||
| w, h, err := ppmHeader(br) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| img := image.NewRGBA(image.Rect(0, 0, w, h)) | ||
| row := make([]byte, 3*w) | ||
| for y := range h { | ||
| if _, err := io.ReadFull(br, row); err != nil { | ||
| return nil, errors.New("truncated PPM page") | ||
| } | ||
| pix := img.Pix[y*img.Stride : (y+1)*img.Stride] | ||
| for x := range w { | ||
| pix[4*x], pix[4*x+1], pix[4*x+2], pix[4*x+3] = row[3*x], row[3*x+1], row[3*x+2], 255 | ||
| } | ||
| } | ||
| return img, nil | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.