Skip to content

feat: download PDF submission files with annotations - #8179

Open
YheChen wants to merge 4 commits into
MarkUsProject:masterfrom
YheChen:feat/download-pdf-annotation
Open

YheChen wants to merge 4 commits into
MarkUsProject:masterfrom
YheChen:feat/download-pdf-annotation

Conversation

@YheChen

@YheChen YheChen commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

Proposed Changes

Closes #7907.

Downloading a submission file with annotations included currently skips annotations on PDFs — the existing code path wraps each annotation in the file's language comment syntax, which only makes sense for text files. The download modal says as much ("Warning: Annotations on PDFs are not currently downloaded.").

PDF submission files now get their annotations added as PDF "sticky note" (/Subtype /Text) annotations anchored at the annotation's location, so graders' comments are visible when the downloaded file is opened in a PDF viewer. This applies both to downloading a single file and to the zipped download of all files.

A few implementation notes:

  • Prawn cannot modify an existing PDF, so (as in Result#generate_print_pdf) the sticky notes are built in a separate, otherwise empty document with one page per page of the submission file, using the undocumented Prawn::Document#text_annotation.
  • The notes are then transplanted onto the submission's pages by copying the :Annots entries directly, rather than by overlaying pages: CombinePDF drops annotations when injecting one page into another (the concat is commented out in the gem), so the overlay approach used for printing loses them.
  • Annotation coordinates are stored as a fraction of the page as it is displayed, measured from its top left corner, so they are scaled, flipped, and rotated back into PDF user space when the page carries a :Rotate entry — scanned submissions do, since SplitPdfJob sets it (see feat: option to correct sideways scans #8103).
  • Per discussion, Markdown/LaTeX in annotation text is not rendered, matching the existing text file download.

The download warning has been updated to name images, which remain unsupported.

Screenshots of your changes (if applicable)

The only visible UI change is the download modal's warning, which now reads "Warning: Annotations on images are not currently downloaded." The downloaded PDFs themselves show a standard note icon at each annotation's location, which opens a popup with the annotation number and text.

Type of Change

Type Applies?
🚨 Breaking change (fix or feature that would cause existing functionality to change)
New feature (non-breaking change that adds functionality) X
🐛 Bug fix (non-breaking change that fixes an issue)
🎨 User interface change (change to user interface; provide screenshots) X (download warning text only)
♻️ Refactoring (internal change to codebase, without changing functionality) X (shared annotation text formatting)
🚦 Test update (change that only adds or modifies tests)
📦 Dependency update (change that updates a dependency)
📖 Documentation update (change that updates documentation)
🔧 Internal (change that only affects developers or continuous integration)

Checklist

Before opening your pull request:

  • I have performed a self-review of my changes.
  • I have added tests for my changes, if applicable.
  • I have updated the project documentation, if applicable.
  • If this is my first contribution, I have added myself to the list of contributors.

After opening your pull request:

  • I have updated the project Changelog (this is required for all changes).
  • I have verified that the pre-commit.ci checks have passed.
  • I have verified that the CI tests have passed.
  • I have reviewed the test coverage changes reported by Coveralls.
  • I have requested a review from a project maintainer.

Questions and Comments

  • The sticky note is a fixed 20pt square centred on the annotation's top left corner, rather than the annotation's full bounding box. Viewers disagree on how to size the note icon (Acrobat draws a fixed icon at the rect's top left; pdf.js scales it to the rect), so a large annotated region would otherwise render as a giant icon in some viewers. Happy to change this if you'd prefer the note to cover the region.
  • Annotations with a deduction include the criterion and deduction in the note text, matching the text file download.

🤖 Generated with Claude Code

YheChen and others added 2 commits September 21, 2026 21:23
Downloading a submission file with annotations included skipped annotations
on PDFs: the existing code path wraps each annotation in the file's language
comment syntax, which only makes sense for text files.

PDF submission files now get their annotations added as PDF "sticky note"
text annotations anchored at the annotation's location, so they are visible
when the downloaded file is opened in a PDF viewer. Prawn cannot modify an
existing PDF, so the notes are built in a separate document and their :Annots
entries are copied onto the submission's pages, since CombinePDF drops
annotations when injecting one page into another.

Annotation coordinates are stored as a fraction of the page as it is
displayed, so they are flipped and rotated back into PDF user space for pages
that carry a :Rotate entry, as scanned submissions do.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coveralls

coveralls commented Sep 22, 2026

Copy link
Copy Markdown
Collaborator

Coverage Report for CI Build 35929381312

Coverage increased (+0.01%) to 90.803%

Details

  • Coverage increased (+0.01%) from the base build.
  • Patch coverage: 4 uncovered changes across 1 file (98 of 102 lines covered, 96.08%).
  • No coverage regressions found.

Uncovered Changes

File Changed Covered %
app/models/submission_file.rb 37 33 89.19%
Total (2 files) 102 98 96.08%

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 52980
Covered Lines: 49141
Line Coverage: 92.75%
Relevant Branches: 2517
Covered Branches: 1252
Branch Coverage: 49.74%
Branches in Coverage %: Yes
Coverage Strength: 128.11 hits per line

💛 - Coveralls

Comment thread app/models/submission_file.rb Outdated
doc.start_new_page
annotations_by_page.fetch(index + 1, []).each do |annotation|
doc.text_annotation(pdf_annotation_rect(page, annotation),
"#{annotation.annotation_number}. #{annotation_content(annotation)}",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To avoid possible conflicts with ordered list syntax, let's use the display format (#1), (#2), etc.

Comment thread app/models/submission_file.rb Outdated
annotations_by_page.fetch(index + 1, []).each do |annotation|
doc.text_annotation(pdf_annotation_rect(page, annotation),
"#{annotation.annotation_number}. #{annotation_content(annotation)}",
Name: :Comment, Open: false)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see in the source code there's an option for a "last updated" time. Please include this information in the metadata. I happened to notice that Firefox seems to use this information when displaying annotations.

Image

YheChen and others added 2 commits September 23, 2026 18:29
Use the display format "(MarkUsProject#1)" for annotation numbers in PDF sticky notes,
rather than "1.", which can be mistaken for ordered list syntax.

Set the :M (modification date) entry on each sticky note so PDF viewers can
show when the annotation was last changed; Firefox falls back to the epoch
when it is absent. An annotation's text can be edited after the annotation
itself was placed, so the later of the two timestamps is used.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ability to download PDFs with annotations

3 participants