Skip to content

Commit 361c7b2

Browse files
Merge pull request #7 from TheEightBot/copilot/fix-fitpolicy-height-issue
2 parents 806f902 + 20241b3 commit 361c7b2

6 files changed

Lines changed: 160 additions & 26 deletions

File tree

samples/MauiPdfViewerSample/PdfTestPage.xaml

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
</ResourceDictionary>
4545
</ContentPage.Resources>
4646

47-
<ScrollView BackgroundColor="{StaticResource SurfaceColor}">
47+
<ScrollView BackgroundColor="{StaticResource SurfaceColor}">
4848
<VerticalStackLayout Spacing="20"
4949
Padding="20,20,20,32">
5050
<!-- Lean hero copy so phones see controls immediately -->
@@ -157,14 +157,15 @@
157157
</Border.StrokeShape>
158158
<pdf:PdfView x:Name="PdfViewer"
159159
Source="sample.pdf"
160-
HeightRequest="460"
160+
HeightRequest="200"
161161
HorizontalOptions="FillAndExpand"
162162
VerticalOptions="FillAndExpand"
163163
EnableZoom="True"
164164
EnableSwipe="True"
165165
EnableTapGestures="False"
166166
PageSpacing="10"
167167
EnableAntialiasing="True"
168+
FitPolicy="Height"
168169
UseBestQuality="True"
169170
EnableAnnotationRendering="True"
170171
DocumentLoaded="OnDocumentLoaded"
@@ -239,6 +240,17 @@
239240
Clicked="OnToggleAnnotationsClicked"
240241
Grid.Column="1"/>
241242
</Grid>
243+
<Grid ColumnDefinitions="Auto,*" ColumnSpacing="12">
244+
<Label Text="Fit policy"
245+
TextColor="#CBD5F5"
246+
VerticalOptions="Center"
247+
Grid.Column="0"/>
248+
<Button x:Name="ToggleFitPolicyButton"
249+
Style="{StaticResource GhostButtonStyle}"
250+
Text="Height"
251+
Clicked="OnToggleFitPolicyClicked"
252+
Grid.Column="1"/>
253+
</Grid>
242254
<Grid ColumnDefinitions="Auto,*" ColumnSpacing="12">
243255
<Label Text="Tap gestures"
244256
TextColor="#CBD5F5"
@@ -307,5 +319,5 @@
307319
</VerticalStackLayout>
308320
</toolkit:Expander>
309321
</VerticalStackLayout>
310-
</ScrollView>
322+
</ScrollView>
311323
</ContentPage>

samples/MauiPdfViewerSample/PdfTestPage.xaml.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,21 @@ public partial class PdfTestPage : ContentPage
1414
Colors.Beige
1515
};
1616

17+
// Start at Height to match the XAML default (FitPolicy="Height")
18+
private int _fitPolicyIndex = 1;
19+
private readonly FitPolicy[] _fitPolicies = new[]
20+
{
21+
FitPolicy.Width,
22+
FitPolicy.Height,
23+
FitPolicy.Both
24+
};
25+
private readonly string[] _fitPolicyNames = new[]
26+
{
27+
"Width",
28+
"Height",
29+
"Both"
30+
};
31+
1732
private int _displayModeIndex = 0;
1833
private readonly PdfDisplayMode[] _displayModes = new[]
1934
{
@@ -140,6 +155,14 @@ private void OnToggleBackgroundClicked(object? sender, EventArgs e)
140155
StatusLabel.Text = $"Background color: {_backgroundColors[_backgroundColorIndex]}";
141156
}
142157

158+
private void OnToggleFitPolicyClicked(object? sender, EventArgs e)
159+
{
160+
_fitPolicyIndex = (_fitPolicyIndex + 1) % _fitPolicies.Length;
161+
PdfViewer.FitPolicy = _fitPolicies[_fitPolicyIndex];
162+
ToggleFitPolicyButton.Text = _fitPolicyNames[_fitPolicyIndex];
163+
StatusLabel.Text = $"Fit policy: {_fitPolicyNames[_fitPolicyIndex]}";
164+
}
165+
143166
// New Phase 5 event handler
144167
private void OnToggleDisplayModeClicked(object? sender, EventArgs e)
145168
{

src/MauiNativePdfView/Platforms/Android/PdfViewAndroid.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,13 @@ private void ConfigureAndLoad(PDFView.Configurator configurator, int pageToResto
320320
configurator.Password(_source.Password);
321321
}
322322

323+
var nativeFitPolicy = _fitPolicy switch
324+
{
325+
Abstractions.FitPolicy.Height => Com.Ahmer.Pdfviewer.Util.FitPolicy.Height,
326+
Abstractions.FitPolicy.Both => Com.Ahmer.Pdfviewer.Util.FitPolicy.Both,
327+
_ => Com.Ahmer.Pdfviewer.Util.FitPolicy.Width,
328+
};
329+
323330
configurator
324331
.EnableSwipe(_enableSwipe)
325332
.EnableDoubleTap(_enableZoom)
@@ -331,6 +338,7 @@ private void ConfigureAndLoad(PDFView.Configurator configurator, int pageToResto
331338
.PageFling(enablePageFling)
332339
.NightMode(false)
333340
.FitEachPage(false)
341+
.PageFitPolicy(nativeFitPolicy)
334342
.EnableAntialiasing(_enableAntialiasing)
335343
.OnLoad(new LoadCompleteListener(this, pageToRestore))
336344
.OnPageChange(new PageChangeListener(this))

src/MauiNativePdfView/Platforms/Android/PdfViewHandler.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,12 @@ protected override void ConnectHandler(PDFView platformView)
7575
{
7676
base.ConnectHandler(platformView);
7777

78-
// Apply initial values
79-
MapSource(this, VirtualView);
78+
// Apply all config properties BEFORE Source so the document loads with the correct settings.
8079
MapEnableZoom(this, VirtualView);
8180
MapEnableSwipe(this, VirtualView);
8281
MapEnableTapGestures(this, VirtualView);
8382
MapEnableLinkNavigation(this, VirtualView);
83+
MapZoom(this, VirtualView);
8484
MapMinZoom(this, VirtualView);
8585
MapMaxZoom(this, VirtualView);
8686
MapPageSpacing(this, VirtualView);
@@ -91,6 +91,9 @@ protected override void ConnectHandler(PDFView platformView)
9191
MapEnableAntialiasing(this, VirtualView);
9292
MapUseBestQuality(this, VirtualView);
9393
MapBackgroundColor(this, VirtualView);
94+
MapEnableAnnotationRendering(this, VirtualView);
95+
// Source is applied last so LoadDocument() picks up all pre-configured properties.
96+
MapSource(this, VirtualView);
9497
}
9598

9699
protected override void DisconnectHandler(PDFView platformView)

src/MauiNativePdfView/Platforms/iOS/PdfViewHandler.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,9 @@ protected override void ConnectHandler(PdfKit.PdfView platformView)
6767
{
6868
base.ConnectHandler(platformView);
6969

70-
// Apply initial property values
70+
// Apply all config properties BEFORE Source so the document loads with the correct settings.
7171
if (_pdfViewWrapper != null && VirtualView != null)
7272
{
73-
_pdfViewWrapper.Source = VirtualView.Source;
7473
_pdfViewWrapper.EnableZoom = VirtualView.EnableZoom;
7574
_pdfViewWrapper.EnableSwipe = VirtualView.EnableSwipe;
7675
_pdfViewWrapper.EnableTapGestures = VirtualView.EnableTapGestures;
@@ -79,13 +78,16 @@ protected override void ConnectHandler(PdfKit.PdfView platformView)
7978
_pdfViewWrapper.MinZoom = VirtualView.MinZoom;
8079
_pdfViewWrapper.MaxZoom = VirtualView.MaxZoom;
8180
_pdfViewWrapper.PageSpacing = VirtualView.PageSpacing;
82-
_pdfViewWrapper.FitPolicy = VirtualView.FitPolicy;
8381
_pdfViewWrapper.DisplayMode = VirtualView.DisplayMode;
8482
_pdfViewWrapper.ScrollOrientation = VirtualView.ScrollOrientation;
8583
_pdfViewWrapper.DefaultPage = VirtualView.DefaultPage;
8684
_pdfViewWrapper.EnableAntialiasing = VirtualView.EnableAntialiasing;
8785
_pdfViewWrapper.UseBestQuality = VirtualView.UseBestQuality;
8886
_pdfViewWrapper.BackgroundColor = VirtualView.BackgroundColor;
87+
// FitPolicy modifies DisplayMode internally, so it must be applied after DisplayMode.
88+
_pdfViewWrapper.FitPolicy = VirtualView.FitPolicy;
89+
// Source is applied last so LoadDocument() picks up all pre-configured properties.
90+
_pdfViewWrapper.Source = VirtualView.Source;
8991
}
9092
}
9193

src/MauiNativePdfView/Platforms/iOS/PdfViewiOS.cs

Lines changed: 104 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace MauiNativePdfView.Platforms.iOS;
1010
/// </summary>
1111
public class PdfViewiOS : IPdfView, IDisposable
1212
{
13-
private readonly PdfKit.PdfView _pdfView;
13+
private readonly NativePdfView _pdfView;
1414
private PdfSource? _source;
1515
private bool _disposed;
1616
private NSObject? _pageChangedObserver;
@@ -21,16 +21,25 @@ public class PdfViewiOS : IPdfView, IDisposable
2121
private bool _documentLoaded = false;
2222
private bool _enableAnnotationRendering = true;
2323
private bool _enableTapGestures = true;
24+
private FitPolicy _fitPolicy = FitPolicy.Width;
25+
private bool _needsFitReapply = false;
2426

2527
public PdfViewiOS()
2628
{
27-
_pdfView = new PdfKit.PdfView
29+
_pdfView = new NativePdfView
2830
{
2931
AutoScales = true,
3032
DisplayMode = PdfKit.PdfDisplayMode.SinglePageContinuous,
3133
DisplayDirection = PdfDisplayDirection.Vertical
3234
};
3335

36+
// Re-apply deferred fit policy once the view has been laid out and has real bounds.
37+
_pdfView.LayoutSubviewsAction = () =>
38+
{
39+
if (_needsFitReapply)
40+
ApplyFitPolicy();
41+
};
42+
3443
// Subscribe to page change notifications
3544
_pageChangedObserver = NSNotificationCenter.DefaultCenter.AddObserver(
3645
PdfKit.PdfView.PageChangedNotification,
@@ -146,26 +155,81 @@ public int PageSpacing
146155

147156
public FitPolicy FitPolicy
148157
{
149-
get => _pdfView.AutoScales ? FitPolicy.Width : FitPolicy.Both;
158+
get => _fitPolicy;
150159
set
151160
{
152-
switch (value)
153-
{
154-
case FitPolicy.Width:
155-
_pdfView.AutoScales = true;
156-
_pdfView.DisplayMode = PdfKit.PdfDisplayMode.SinglePageContinuous;
157-
break;
158-
case FitPolicy.Height:
159-
_pdfView.AutoScales = true;
160-
_pdfView.DisplayMode = PdfKit.PdfDisplayMode.SinglePage;
161-
break;
162-
case FitPolicy.Both:
163-
_pdfView.AutoScales = false;
164-
break;
165-
}
161+
_fitPolicy = value;
162+
ApplyFitPolicy();
163+
}
164+
}
165+
166+
private void ApplyFitPolicy()
167+
{
168+
switch (_fitPolicy)
169+
{
170+
case FitPolicy.Width:
171+
_pdfView.AutoScales = true;
172+
_pdfView.DisplayMode = PdfKit.PdfDisplayMode.SinglePageContinuous;
173+
_needsFitReapply = false;
174+
break;
175+
176+
case FitPolicy.Height:
177+
// PdfKit AutoScales only fits width; calculate scale manually.
178+
_pdfView.AutoScales = false;
179+
_pdfView.DisplayMode = PdfKit.PdfDisplayMode.SinglePageContinuous;
180+
_needsFitReapply = !SetManualScale(fitWidth: false, fitHeight: true);
181+
break;
182+
183+
case FitPolicy.Both:
184+
// Fit to the smaller of the width/height scale factors so the whole page
185+
// is visible. Use SinglePageContinuous to avoid SinglePage's inflated
186+
// internal scroll content size.
187+
_pdfView.AutoScales = false;
188+
_pdfView.DisplayMode = PdfKit.PdfDisplayMode.SinglePageContinuous;
189+
_needsFitReapply = !SetManualScale(fitWidth: true, fitHeight: true);
190+
break;
166191
}
167192
}
168193

194+
/// <summary>
195+
/// Computes and applies ScaleFactor so the current page fits the requested axes.
196+
/// Returns <c>true</c> on success, <c>false</c> if the view or page isn't ready yet
197+
/// (caller should set <see cref="_needsFitReapply"/> and retry on next layout pass).
198+
/// </summary>
199+
private bool SetManualScale(bool fitWidth, bool fitHeight)
200+
{
201+
var page = _pdfView.CurrentPage;
202+
if (page == null)
203+
return false;
204+
205+
var viewBounds = _pdfView.Bounds;
206+
if (viewBounds.Width <= 0 || viewBounds.Height <= 0)
207+
return false;
208+
209+
var pageRect = page.GetBoundsForBox(PdfDisplayBox.Media);
210+
if (pageRect.Width <= 0 || pageRect.Height <= 0)
211+
return false;
212+
213+
nfloat scale;
214+
if (fitWidth && fitHeight)
215+
{
216+
var scaleW = (nfloat)(viewBounds.Width / pageRect.Width);
217+
var scaleH = (nfloat)(viewBounds.Height / pageRect.Height);
218+
scale = scaleW < scaleH ? scaleW : scaleH;
219+
}
220+
else if (fitHeight)
221+
{
222+
scale = (nfloat)(viewBounds.Height / pageRect.Height);
223+
}
224+
else
225+
{
226+
scale = (nfloat)(viewBounds.Width / pageRect.Width);
227+
}
228+
229+
_pdfView.ScaleFactor = scale;
230+
return true;
231+
}
232+
169233
public Abstractions.PdfDisplayMode DisplayMode
170234
{
171235
get
@@ -358,7 +422,13 @@ private void LoadDocument()
358422
}
359423
}
360424

361-
MainThread.BeginInvokeOnMainThread(() => _pdfView.Document = document);
425+
MainThread.BeginInvokeOnMainThread(() =>
426+
{
427+
_pdfView.Document = document;
428+
// Re-apply fit policy now that the document is loaded and
429+
// the view has valid bounds to calculate scale against.
430+
ApplyFitPolicy();
431+
});
362432

363433
// Get document metadata
364434
var pageCount = (int)document.PageCount;
@@ -553,6 +623,22 @@ public void Dispose()
553623
_pdfView?.Dispose();
554624
}
555625

626+
/// <summary>
627+
/// Custom PdfView subclass that fires a callback on each layout pass, allowing
628+
/// <see cref="PdfViewiOS"/> to defer fit-policy scale calculations until the view
629+
/// has non-zero bounds (which isn't guaranteed when the document first loads).
630+
/// </summary>
631+
private class NativePdfView : PdfKit.PdfView
632+
{
633+
internal Action? LayoutSubviewsAction { get; set; }
634+
635+
public override void LayoutSubviews()
636+
{
637+
base.LayoutSubviews();
638+
LayoutSubviewsAction?.Invoke();
639+
}
640+
}
641+
556642
/// <summary>
557643
/// Delegate implementation to intercept link clicks in PDFView.
558644
/// </summary>

0 commit comments

Comments
 (0)