Repository navigation
ops: turn a page by any angle, growing the paper to hold it - #83
Merged
Merged
Conversation
Doc.Turn and `pdfops turn`, the arbitrary-angle half that Rotate refuses. Rotate writes a number on the page -- /Rotate, a multiple of ninety -- and Turn rewrites the page onto a sheet of its own. ⛔ THE PAPER GROWS AND NOTHING IS CUT OFF, which is the whole difference from Deskew. A deskew is a correction of a fraction of a degree on a page whose text sits well inside its margins, so it keeps the paper it was given; a turn by thirty degrees loses two corners if the paper does not grow with it. The sheet is the turned page's bounding box -- w|cos| + h|sin| across, w|sin| + h|cos| down, BentoPDF's own arithmetic -- and the page is centred on it. The test maps all four corners for angles from -350 to 350 degrees: each must land inside the sheet, and each edge must be touched, or the box is bigger than it needs to be. A box built from the wrong pair of terms is right at 0, 45 and 90 degrees and wrong everywhere between, which is why it is swept rather than sampled. An angle that is a multiple of ninety goes through /Rotate and nothing else happens: no transformation, no new content stream, a page whose text was selectable stays selectable. That is both cheaper and what the reference does. ⛔⛔ POSITIVE IS CLOCKWISE, AND THE REFERENCE CONTRADICTS ITSELF ABOUT IT. rotate-custom.md says "Positive values rotate clockwise". Its code writes a multiple of ninety as /Rotate, which ISO 32000-1 Table 30 defines as degrees CLOCKWISE, and sends every other angle through pdf-lib's drawPage, which concatenates [cos sin -sin cos] -- a COUNTER-clockwise turn in a space whose y counts upwards. The two arms turn opposite ways and the seam is at exactly 90. Both are clockwise here: it is what its documentation promises, what the specification means by a rotation, and what Doc.Rotate has always done. The gate is a render, because every assertion written on how FAR a page turned survives a sign error. A mark at the top of a square page must come back at the right-hand edge after +90, the way a clock's hand goes from twelve to three -- and the seam is crossed on purpose, at 89, 90 and 91 degrees, with two questions: how far ACROSS the page the mark is cannot tell 89 from 91 but catches a quarter-turn arm going the other way, and how far DOWN it is does separate them and catches the matrix arm. Both go red when the sign in turnOntoSheet is flipped; that was run. ⛔ And quarterTurns refuses an angle whose quarter-circles do not fit in an int instead of converting: nothing a person types reaches 2^31 degrees, but a file can hold anything. ok github.com/go-pdfkit/ops 14.9s coverage: 100.0% of statements; covblocks: 0 blocks uncovered. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Doc.Turnandpdfops turn: the arbitrary-angle half thatRotaterefuses.Rotatewrites a number on the page —/Rotate, a multiple of ninety — andTurnrewrites the page onto a sheet of its own.Ported from BentoPDF's
rotate-custom(docs/tools/rotate-custom.md,src/js/logic/rotate-custom-page.ts), which is the remaining Rotate byCustom Degrees row of the gap table.
The paper grows and nothing is cut off
That is the whole difference from
Deskew. A deskew is a correction of afraction of a degree on a page whose text sits well inside its margins, so it
keeps the paper it was given; a turn by thirty degrees loses two corners if the
paper does not grow with it.
The sheet is the turned page's bounding box —
w·|cos θ| + h·|sin θ|across,w·|sin θ| + h·|cos θ|down, the reference's own arithmetic — and the page iscentred on it. The test maps all four corners for angles from −350° to 350°:
each must land inside the sheet, and each edge must be touched, or the box
is bigger than it needs to be. A box built from the wrong pair of terms is right
at 0°, 45° and 90° and wrong everywhere between, which is why it is swept rather
than sampled.
An angle that is a multiple of ninety goes through
/Rotateand nothing elsehappens: no transformation, no new content stream, a page whose text was
selectable stays selectable. Cheaper, and what the reference does too.
⛔ Positive is clockwise, and the reference contradicts itself about it
rotate-custom.mdsays "Positive values rotate clockwise". Its code writes amultiple of ninety as
/Rotate— which ISO 32000-1 Table 30 defines as degreesclockwise — and sends every other angle through pdf-lib's
drawPage, whichconcatenates
[cos sin −sin cos]: a counter-clockwise turn in a space whosey counts upwards. The two arms turn opposite ways, and the seam is at exactly
90°, where the tool switches from one to the other.
Both arms are clockwise here. That is what its documentation promises, what the
specification means by a rotation, and what
Doc.Rotatein this package hasalways done — a library in which
Turn(90)andRotate(90)disagreed would bea worse trap than the one being ported.
The gate is a render, and it was proved by breaking it
Every assertion written on how far a page turned survives a sign error: a
page turned 30° the wrong way is exactly as far from straight as one turned 30°
the right way. So the test asks where the ink ended up. A mark at the top of
a square page must come back at the right-hand edge after +90°, the way a
clock's hand goes from twelve to three.
The seam is crossed on purpose, at 89°, 90° and 91°, with two questions,
because neither alone can see it:
catches a quarter-turn arm going the other way, which would put the mark at
nine o'clock;
Both go red when the sign in
turnOntoSheetis flipped. That was run, notpredicted.
⛔
quarterTurnsrefuses an angle whose quarter-circles do not fit in anintrather than converting: nothing a person types reaches 2³¹ degrees, but a file
can hold anything, and that is the kind of arithmetic that turns a document into
a panic.
ok github.com/go-pdfkit/ops 14.9s coverage: 100.0% of statements,covblocks: 0 blocks uncovered.🤖 Generated with Claude Code