Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ module github.com/go-pdfkit/app
go 1.26.4

require (
github.com/go-pdfkit/ops v0.5.0
github.com/go-pdfkit/reader v0.4.1
github.com/go-pdfkit/render v0.7.0
github.com/go-gfx/gfx v0.10.0
github.com/go-pdfkit/forms v0.2.1
github.com/go-pdfkit/ops v0.7.0
github.com/go-pdfkit/reader v0.5.0
github.com/go-pdfkit/render v0.10.0
github.com/go-widgets/painter v0.11.0
github.com/go-widgets/toolkit v0.250.0
github.com/go-widgets/webcanvas v0.1.0
Expand All @@ -16,14 +18,12 @@ require (
github.com/coder/websocket v1.8.15 // indirect
github.com/go-crdt/collab v0.25.0 // indirect
github.com/go-crdt/crdt v0.31.0 // indirect
github.com/go-gfx/gfx v0.10.0 // indirect
github.com/go-iconoir/iconoir v0.2.0 // indirect
github.com/go-images/images v0.0.0-20260811115337-bc5d586f8e38 // indirect
github.com/go-opentype/fonts v0.9.0 // indirect
github.com/go-opentype/opentype v0.9.0 // indirect
github.com/go-opentype/opentype v0.10.0 // indirect
github.com/go-opentype/shape v0.5.0 // indirect
github.com/go-pdfkit/forms v0.2.1 // indirect
github.com/go-pdfkit/pdffont v0.2.0 // indirect
github.com/go-pdfkit/pdffont v0.3.0 // indirect
github.com/go-richdoc/richdoc v0.2.0 // indirect
github.com/go-typeset/bidi v0.3.0 // indirect
github.com/go-widgets/mvvm v0.5.0 // indirect
Expand Down
24 changes: 10 additions & 14 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,20 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-opentype/fonts v0.9.0 h1:slB6OB3riLyUPrOxqXe0s6/AzdenF1TDvCN8N87hhQk=
github.com/go-opentype/fonts v0.9.0/go.mod h1:C6yQL2apHItfEZ5hztpsHF0S5mlX/hklLlq/Z5fRG/g=
github.com/go-opentype/opentype v0.9.0 h1:GFgcJ3nwTDp4NJr5O+Paw7lhZx5Jv/R+noZwvhYDlkM=
github.com/go-opentype/opentype v0.9.0/go.mod h1:AOixevJf7XQaH7+WG+OMIOZEbYPXfMqklVk26Y6YTUU=
github.com/go-opentype/opentype v0.10.0 h1:cZVMZ3RVkcijXmxlmpqyVkjlNc33aSB2ugKVWYvoLUg=
github.com/go-opentype/opentype v0.10.0/go.mod h1:AOixevJf7XQaH7+WG+OMIOZEbYPXfMqklVk26Y6YTUU=
github.com/go-opentype/shape v0.5.0 h1:jHNaOMHNBdDj5EixOevlrrsi92svMxvVMKN7GYaPfxo=
github.com/go-opentype/shape v0.5.0/go.mod h1:3ImRYNIj6zpwWQ/DV3BWhgMFfmzDITH6XpZw2auQHTo=
github.com/go-pdfkit/forms v0.2.1 h1:INa2GwAadxEhcXvBe7zJYgapYwaGn9WIaLxUJU/lm6A=
github.com/go-pdfkit/forms v0.2.1/go.mod h1:LORxkdP4FVULFk0/PA0CdPZc1dZF9zTSaHrtfM7IYaw=
github.com/go-pdfkit/ops v0.4.0 h1:2WeYwDhN2OyoYtjYCdgm7gWGVNmiu+8OwOKEhyb3Cp0=
github.com/go-pdfkit/ops v0.4.0/go.mod h1:X89phxHICCYl6+zn3L6YXH3mRLBlsLdPYuDCryGLXzI=
github.com/go-pdfkit/ops v0.5.0 h1:b1LF7hF9QsqtF2AC0G7HjwulNCXJ0AOE7zaUSzswXrg=
github.com/go-pdfkit/ops v0.5.0/go.mod h1:Is3FBR2NcCUEzNt4eowdsVa+P0SBtGtcxK8lDlOeyrk=
github.com/go-pdfkit/pdffont v0.2.0 h1:yAp/oR5Z2kkqs4r0GWMalZMC7rc7XSCZXgwIypbpMWM=
github.com/go-pdfkit/pdffont v0.2.0/go.mod h1:y4vo5DgT95e57C3XxIWfA/xss+x6RwZwyj6KWdJc86s=
github.com/go-pdfkit/reader v0.4.0 h1:qPbNZSO+Xl+4NBvQoV1PYt7HlqHaEAQ1tUc6/IL8JAU=
github.com/go-pdfkit/reader v0.4.0/go.mod h1:fQFOVfCMUui1AdvD4qhimdyvvNr9KvvJ1S7IuKZjyV8=
github.com/go-pdfkit/reader v0.4.1 h1:pRxFqRjsn7H/VsGfWb9nYWyFuDgTU2Pjmoq/f5mgVq4=
github.com/go-pdfkit/reader v0.4.1/go.mod h1:fQFOVfCMUui1AdvD4qhimdyvvNr9KvvJ1S7IuKZjyV8=
github.com/go-pdfkit/render v0.7.0 h1:wTEvYxcJkYFYCXJMzrGcMUo0gVgp4A+VqAsTS/hyFq8=
github.com/go-pdfkit/render v0.7.0/go.mod h1:RCleIv5QnEDbh9kILa7J8pNUNDUyhsx5p8hv06lpcLk=
github.com/go-pdfkit/ops v0.7.0 h1:W8sK//0nfs/OACvjzvw/GK5ywtd90eDoYcdv74GxW8M=
github.com/go-pdfkit/ops v0.7.0/go.mod h1:Is3FBR2NcCUEzNt4eowdsVa+P0SBtGtcxK8lDlOeyrk=
github.com/go-pdfkit/pdffont v0.3.0 h1:G5DKcAmsZJ0e17QhSrcUaL7PKEXKjUx4P/iGMl7bAbI=
github.com/go-pdfkit/pdffont v0.3.0/go.mod h1:bfmNLna1l1CljNX/Utg55YzFylovfmI7sJnvgA3bzKI=
github.com/go-pdfkit/reader v0.5.0 h1:DaJ5C6eKXPRG9Cdu+olA/0KLmsQgyHEC1coteQQuBxU=
github.com/go-pdfkit/reader v0.5.0/go.mod h1:fQFOVfCMUui1AdvD4qhimdyvvNr9KvvJ1S7IuKZjyV8=
github.com/go-pdfkit/render v0.10.0 h1:2NXlxhb7bDWFfgWpMe1bfZXbefhEkLSeH99xWI8B1uk=
github.com/go-pdfkit/render v0.10.0/go.mod h1:ZNuGzwPk1m3kmJIg9X0bepSGrAmmCb5nTMbDEbHuFFA=
github.com/go-richdoc/richdoc v0.2.0 h1:z9cLox9MoInZL6fIlweMzgDT/VqgnB2ZucSIEaRFglY=
github.com/go-richdoc/richdoc v0.2.0/go.mod h1:aCX8ulqg5CoKqSWgWeEWul1Oj0d2VxQeaQNI0+jsm7c=
github.com/go-typeset/bidi v0.3.0 h1:4fjGjejvjE2LzLNzY4si8PkVO321NcsKIiANhWT3jF4=
Expand Down
28 changes: 26 additions & 2 deletions scene.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
package main

import (
"errors"
"fmt"
"time"

"github.com/go-pdfkit/ops"
"github.com/go-pdfkit/reader"
Expand Down Expand Up @@ -346,15 +348,37 @@ func (s *state) renderPage() {
if s.at < 1 {
s.at = 1
}
img, err := drawPage(src, s.at, render.Options{Scale: s.fitScale(src)})
if err != nil {
img, err := drawPage(src, s.at, render.Options{
Scale: s.fitScale(src),
MaxDuration: pageBudget,
})
// A page that ran out of time comes back as far as it got, which is worth
// showing: somebody scrolling would rather see most of a figure than a
// sentence saying there was one. Anything else, and there is no picture.
partial := errors.Is(err, render.ErrTimedOut) && img != nil
if err != nil && !partial {
s.view = toolkit.NewFrame(toolkit.NewLabel("this page cannot be drawn: " + err.Error()))
return
}
if partial {
// Showing half a page without saying so would be the one thing worse
// than showing nothing, so the status line says which it is. This is
// the only place renderPage writes the note, and it overwrites
// whatever was there, because what is on the screen right now matters
// more than what happened before it.
s.note = fmt.Sprintf("this page was still being drawn after %s; this is as far as it got", pageBudget)
}
s.page = toolkit.NewImageFit(img.Pix, img.W, img.H)
s.view = toolkit.NewFrame(s.page)
}

// pageBudget is how long one page may be drawn for before what has been drawn
// so far is shown instead. A handful of pages take minutes — of the 59 432
// pages the renderer was measured against, 1 131 were still going after twenty
// seconds and one took two hundred and seventy-three — and a window that stops
// answering for that long reads as broken rather than as busy.
const pageBudget = 5 * time.Second

// fitScale is how much to magnify the page so that it fills the view without
// spilling out of it.
func (s *state) fitScale(src *reader.Document) float64 {
Expand Down
59 changes: 59 additions & 0 deletions scene_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import (
"errors"
"strings"
"testing"
"time"

"github.com/go-gfx/gfx/raster"
"github.com/go-pdfkit/ops"
"github.com/go-pdfkit/reader"
"github.com/go-pdfkit/render"
"github.com/go-widgets/toolkit"
)

Expand Down Expand Up @@ -588,3 +591,59 @@ func TestAControlIsWideEnoughForItsName(t *testing.T) {
t.Errorf("the strip is %d wide and the surface is %d", total, surfaceW-2*margin)
}
}

func TestAPageThatRanOutOfTimeIsShownAsFarAsItGot(t *testing.T) {
// The renderer is given a budget, and a page that overruns it comes back
// half drawn together with render.ErrTimedOut. Half a page is worth more
// to somebody scrolling than a sentence saying there was one, so the
// workbench shows it rather than the error.
s, _ := opened(t, 1)
was := drawPage
t.Cleanup(func() { drawPage = was })
var budget time.Duration
drawPage = func(_ *reader.Document, _ int, opt render.Options) (*raster.Image, error) {
budget = opt.MaxDuration
return raster.New(4, 4), render.ErrTimedOut
}

s.refresh()
if budget <= 0 {
t.Error("the renderer was given no budget at all")
}
if s.page == nil {
t.Fatal("the part of the page that was drawn was thrown away")
}
if !strings.Contains(s.note, "as far as it got") {
t.Errorf("half a page was shown without saying so; the status line said %q", s.note)
}
if got := s.statusLine(); !strings.Contains(strings.Join(got, "|"), "as far as it got") {
t.Errorf("the status line does not carry it: %q", got)
}
buf := buffer()
s.draw(buf)
if inked(buf, s.theme.Background) == 0 {
t.Error("nothing reached the screen")
}
}

func TestAPageThatRanOutOfTimeWithNothingDrawnSaysSo(t *testing.T) {
// Coming back timed out and empty is not something the renderer does,
// but if it did there is no picture to show, so the view has to fall
// back on saying why rather than dereferencing nothing.
s, _ := opened(t, 1)
was := drawPage
t.Cleanup(func() { drawPage = was })
drawPage = func(*reader.Document, int, render.Options) (*raster.Image, error) {
return nil, render.ErrTimedOut
}

s.refresh()
if s.page != nil {
t.Error("a page was drawn from nothing")
}
buf := buffer()
s.draw(buf)
if inked(buf, s.theme.Background) == 0 {
t.Error("the reason was not drawn")
}
}
Loading