handle page rotation by updating initial TransformationMatrix

This commit is contained in:
BobLd 2020-06-02 16:12:24 +01:00
parent 6e773446df
commit 33f92cd11c
2 changed files with 68 additions and 66 deletions

View File

@ -66,63 +66,6 @@
Value = rotation;
}
[Pure]
internal PdfRectangle Rotate(PdfRectangle rectangle, PdfVector pageSize)
{
// TODO: this is a bit of a hack because I don't understand matrices
/* There should be a single Affine Transform we can apply to any point resulting
* from a content stream operation which will rotate the point and translate it back to
* a point where the origin is in the page's lower left corner.
*
* For example this matrix represents a (clockwise) rotation and translation:
* [ cos sin tx ]
* [ -sin cos ty ]
* [ 0 0 1 ]
*
* The values of tx and ty are those required to move the origin back to the expected origin (lower-left).
* The corresponding values should be:
* Rotation: 0 90 180 270
* tx: 0 0 w w
* ty: 0 h h 0
*
* Where w and h are the page width and height after rotation.
*/
double cos, sin;
double dx = 0, dy = 0;
switch (Value)
{
case 0:
return rectangle;
case 90:
cos = 0;
sin = 1;
dy = pageSize.Y;
break;
case 180:
cos = -1;
sin = 0;
dx = pageSize.X;
dy = pageSize.Y;
break;
case 270:
cos = 0;
sin = -1;
dx = pageSize.X;
break;
default:
throw new InvalidOperationException($"Invalid value for rotation: {Value}.");
}
PdfPoint Multiply(PdfPoint pt)
{
return new PdfPoint((pt.X * cos) + (pt.Y * sin) + dx,
(pt.X * -sin) + (pt.Y * cos) + dy);
}
return new PdfRectangle(Multiply(rectangle.TopLeft), Multiply(rectangle.TopRight),
Multiply(rectangle.BottomLeft), Multiply(rectangle.BottomRight));
}
/// <inheritdoc />
public override int GetHashCode()

View File

@ -88,7 +88,7 @@
IPdfTokenScanner pdfScanner,
IPageContentParser pageContentParser,
IFilterProvider filterProvider,
ILog log,
ILog log,
bool clipPaths,
PdfVector pageSize)
{
@ -108,10 +108,75 @@
var clippingPath = new PdfPath() { clippingSubpath };
clippingPath.SetClipping(FillingRule.NonZeroWinding);
graphicsStack.Push(new CurrentGraphicsState() { CurrentClippingPath = clippingPath });
graphicsStack.Push(new CurrentGraphicsState()
{
CurrentTransformationMatrix = GetInitialMatrix(),
CurrentClippingPath = clippingPath
});
ColorSpaceContext = new ColorSpaceContext(GetCurrentState, resourceStore);
}
[System.Diagnostics.Contracts.Pure]
private TransformationMatrix GetInitialMatrix()
{
// TODO: this is a bit of a hack because I don't understand matrices
// TODO: use MediaBox (i.e. pageSize) or CropBox?
/*
* There should be a single Affine Transform we can apply to any point resulting
* from a content stream operation which will rotate the point and translate it back to
* a point where the origin is in the page's lower left corner.
*
* For example this matrix represents a (clockwise) rotation and translation:
* [ cos sin tx ]
* [ -sin cos ty ]
* [ 0 0 1 ]
* Warning: rotation is counter-clockwise here
*
* The values of tx and ty are those required to move the origin back to the expected origin (lower-left).
* The corresponding values should be:
* Rotation: 0 90 180 270
* tx: 0 0 w w
* ty: 0 h h 0
*
* Where w and h are the page width and height after rotation.
*/
double cos, sin;
double dx = 0, dy = 0;
switch (rotation.Value)
{
case 0:
cos = 1;
sin = 0;
break;
case 90:
cos = 0;
sin = 1;
dy = pageSize.Y;
break;
case 180:
cos = -1;
sin = 0;
dx = pageSize.X;
dy = pageSize.Y;
break;
case 270:
cos = 0;
sin = -1;
dx = pageSize.X;
break;
default:
throw new InvalidOperationException($"Invalid value for page rotation: {rotation.Value}.");
}
return new TransformationMatrix(
cos, -sin, 0,
sin, cos, 0,
dx, dy, 1);
}
public PageContent Process(int pageNumberCurrent, IReadOnlyList<IGraphicsStateOperation> operations)
{
pageNumber = pageNumberCurrent;
@ -225,19 +290,13 @@
}
var boundingBox = font.GetBoundingBox(code);
var transformedGlyphBounds = PerformantRectangleTransformer
.Transform(renderingMatrix, textMatrix, transformationMatrix, boundingBox.GlyphBounds);
var transformedPdfBounds = PerformantRectangleTransformer
.Transform(renderingMatrix, textMatrix, transformationMatrix, new PdfRectangle(0, 0, boundingBox.Width, 0));
if (rotation.Value > 0)
{
transformedGlyphBounds = rotation.Rotate(transformedGlyphBounds, pageSize);
transformedPdfBounds = rotation.Rotate(transformedPdfBounds, pageSize);
}
// If the text rendering mode calls for filling, the current nonstroking color in the graphics state is used;
// if it calls for stroking, the current stroking color is used.
// In modes that perform both filling and stroking, the effect is as if each glyph outline were filled and then stroked in separate operations.