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
5 changes: 5 additions & 0 deletions src/UglyToad.PdfPig.Tests/Integration/StreamProcessorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,11 @@ public override void ModifyClippingIntersect(FillingRule clippingRule)
// No op
}

protected override void ClipToRectangle(PdfRectangle rectangle, FillingRule clippingRule)
{
// No op
}

public override void PaintShading(NameToken shadingName)
{
// No op
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,11 @@ public override void ModifyClippingIntersect(FillingRule clippingRule)
// No op
}

protected override void ClipToRectangle(PdfRectangle rectangle, FillingRule clippingRule)
{
// No op
}

public override void PaintShading(NameToken shadingName)
{
// No op
Expand Down
13 changes: 13 additions & 0 deletions src/UglyToad.PdfPig/Geometry/GeometryExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,19 @@ static double polarAngle(in PdfPoint point1, in PdfPoint point2)
#endregion

#region PdfRectangle
/// <summary>
/// Converts a <see cref="PdfRectangle"/> into its <see cref="PdfPath"/> representation.
/// </summary>
public static PdfPath ToPdfPath(this PdfRectangle rectangle)
{
var clippingSubpath = new PdfSubpath();
clippingSubpath.Rectangle(rectangle.BottomLeft.X,
rectangle.BottomLeft.Y,
rectangle.Width,
rectangle.Height);
return new PdfPath() { clippingSubpath };
}

/// <summary>
/// Whether the point is located inside the rectangle.
/// </summary>
Expand Down
23 changes: 13 additions & 10 deletions src/UglyToad.PdfPig/Graphics/BaseStreamProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,15 +145,8 @@ protected BaseStreamProcessor(
/// </summary>
protected static PdfPath GetInitialClipping(CropBox cropBox)
{
var cropBoxBounds = cropBox.Bounds;

// initiate CurrentClippingPath to cropBox
var clippingSubpath = new PdfSubpath();
clippingSubpath.Rectangle(cropBoxBounds.BottomLeft.X,
cropBoxBounds.BottomLeft.Y,
cropBoxBounds.Width,
cropBoxBounds.Height);
var clippingPath = new PdfPath() { clippingSubpath };
// Initiate CurrentClippingPath to cropBox
var clippingPath = cropBox.Bounds.ToPdfPath();
clippingPath.SetClipping(FillingRule.EvenOdd);
return clippingPath;
}
Expand Down Expand Up @@ -581,7 +574,14 @@ protected virtual void ProcessFormXObject(StreamToken formStream, NameToken xObj
new MemoryInputBytes(contentStream),
ParsingOptions.Logger);

// 3. We don't respect clipping currently.
// 3. Clip according to the form dictionary's BBox entry.
if (formStream.StreamDictionary.TryGet<ArrayToken>(NameToken.Bbox, PdfScanner, out var bboxToken))
{
var points = bboxToken.Data.OfType<NumericToken>().Select(x => x.Double).ToArray();
PdfRectangle bbox = new PdfRectangle(points[0], points[1], points[2], points[3]);
PdfRectangle transformedBox = startState.CurrentTransformationMatrix.Transform(bbox);
ClipToRectangle(transformedBox, FillingRule.EvenOdd); // TODO - Check that Even Odd is valid
}

// 4. Paint the objects.
bool hasCircularReference = HasFormXObjectCircularReference(formStream, xObjectName, operations);
Expand Down Expand Up @@ -668,6 +668,9 @@ protected virtual bool HasFormXObjectCircularReference(StreamToken formStream,
/// <inheritdoc/>
public abstract void ModifyClippingIntersect(FillingRule clippingRule);

/// <inheritdoc/>
protected abstract void ClipToRectangle(PdfRectangle rectangle, FillingRule clippingRule);

/// <inheritdoc/>
public virtual void SetNamedGraphicsState(NameToken stateName)
{
Expand Down
26 changes: 24 additions & 2 deletions src/UglyToad.PdfPig/Graphics/ContentStreamProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,8 @@ public override void ModifyClippingIntersect(FillingRule clippingRule)

if (ParsingOptions.ClipPaths)
{
var currentClipping = GetCurrentState().CurrentClippingPath!;
var graphicsState = GetCurrentState();
var currentClipping = graphicsState.CurrentClippingPath!;
currentClipping.SetClipping(clippingRule);

var newClippings = CurrentPath.Clip(currentClipping, ParsingOptions.Logger);
Expand All @@ -432,11 +433,32 @@ public override void ModifyClippingIntersect(FillingRule clippingRule)
}
else
{
GetCurrentState().CurrentClippingPath = newClippings;
graphicsState.CurrentClippingPath = newClippings;
}
}
}

protected override void ClipToRectangle(PdfRectangle rectangle, FillingRule clippingRule)
{
// https://github.com/apache/pdfbox/blob/f4bfe47de37f6fe69e8f98b164c3546facfd5e91/pdfbox/src/main/java/org/apache/pdfbox/contentstream/PDFStreamEngine.java#L611
var graphicsState = GetCurrentState();
var clip = rectangle.ToPdfPath();
clip.SetClipping(clippingRule);

var currentClipping = graphicsState.CurrentClippingPath!;
currentClipping.SetClipping(clippingRule);
var newClippings = clip.Clip(currentClipping, ParsingOptions.Logger);

if (newClippings is null)
{
ParsingOptions.Logger.Warn("Empty clipping path found. Clipping path not updated.");
}
else
{
graphicsState.CurrentClippingPath = newClippings;
}
}

protected override void RenderInlineImage(InlineImage inlineImage)
{
images.Add(Union<XObjectContentRecord, InlineImage>.Two(inlineImage));
Expand Down
Loading