I'm trying to add highlight annotations to documents using PDFKit on iOS.
let highlight = PDFAnnotation(bounds: selection.bounds(for: page),
forType: PDFAnnotationSubtype.highlight,
withProperties: nil)
highlight.color = color
page.addAnnotation(highlight)
page.displaysAnnotations = true
When adding them using the code above, they appear as two differently shaped layers. When saving them to the PDF file and reopening it they display correctly.
In this screen capture
The top and bottom highlights have been added the same way using the snippet provided here. The top one has been saved to the pdf document and appears as expected when reopening it, the bottom one has just been added.
Does anyone know how to have them display correctly (i.e. like the top one) without resorting to saving and reopening the file?
So this is a know bug in 10.13. There is a workaround by scrolling the page away and then back to the highlight
You can create the highlight using this code:
let page = self.pdfDocument?.page(at: 10)
let bounds = CGRect(x: 85.8660965, y: 786.8891167, width: 298.41, height: 12.1485)
let annotation = PDFAnnotation(bounds: bounds, forType: .highlight, withProperties: nil)
annotation.color = NSColor.blue
page?.addAnnotation(annotation)
Then you need to scroll away from the page and back to the highlight
func annotationScrollHack(page: PDFPage) {
guard let pdfDocument = self.pdfDocument else { return }
//When adding highlights to macOS 10.13 it seems like 2 highlights are added.
//If you scroll to a different page and back the "extra" highlight is removed
//This function scrolls to the first/last page in the book and then back to the current page
//rdar://34784917
let bookScrollView = self.pdfView.documentView?.enclosingScrollView
let currentVisibleRect = bookScrollView?.contentView.documentVisibleRect
if (0 ... 3).contains(pdfDocument.index(for: page)) {
if self.pdfView.canGoToLastPage {
self.pdfView.goToLastPage(self)
}
} else {
if self.pdfView.canGoToFirstPage {
self.pdfView.goToFirstPage(self)
}
}
if let currentVisibleRect = currentVisibleRect {
bookScrollView?.contentView.scroll(to: CGPoint(x: currentVisibleRect.origin.x, y: currentVisibleRect.origin.y))
}
}
Response from Apple:
There is no workaround other than the “hack" they described: scrolling
away then back is the best option. Changing zoom factor and reverting
it might fix it in the same way, but not guaranteed.
I've ended up with this hack method which takes into consideration all cases in my opinion. Hope that will help.
private func hackScroll(to page: PDFPage) {
switch (pdfView.canGoToFirstPage(), pdfView.canGoToLastPage()) {
case (true, false):
pdfView.goToFirstPage(nil)
pdfView.go(to: page)
case (false, true):
pdfView.goToLastPage(nil)
pdfView.go(to: page)
case (false, false):
guard let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let file = "temp.pdf"
/// Save to temp file
let fileURL = dir.appendingPathComponent(file)
pdfView.document?.write(to: fileURL)
/// Read from temp file
guard let document = PDFDocument(url: fileURL) else { return }
pdfView.document = document
pdfView.autoScales = true
default:
pdfView.goToFirstPage(nil)
pdfView.go(to: page)
}
}
Related
I am working on a video editing app where each video gets squared in such a way that no portion of the video gets cropped.For this, in case of portrait video, it contains black portion on left & right and for landscape video, it contains black portion on top & bottom side of the video. Black portions are part of the video, they are not for AVPlayerViewController. Here is the sample,
I need to cover these black portions with some CALayers.
What will be the frame(CGRect) of the CALayer?
I am getting the video dimension with naturalSize property which includes the black portions.
Is there any way to get the video dimension without the black portions?(I mean the dimension of actual video content) or
is there any way to get the CGRect of black area of the video?
func initAspectRatioOfVideo(with fileURL: URL) -> Double {
let resolution = resolutionForLocalVideo(url: fileURL)
guard let width = resolution?.width, let height = resolution?.height else {
return 0
}
return Double(height / width)
}
private func resolutionForLocalVideo(url: URL) -> CGSize? {
guard let track = AVURLAsset(url: url).tracks(withMediaType: AVMediaType.video).first else { return nil }
let size = track.naturalSize.applying(track.preferredTransform)
return CGSize(width: abs(size.width), height: abs(size.height))
}
This is a more concise version of Vlad Pulichev's answer.
var aspectRatio: CGFloat! // use the function to assign your variable
func getVideoResolution(url: String) -> CGFloat? {
guard let track = AVURLAsset(url: URL(string: url)!).tracks(withMediaType: AVMediaType.video).first else { return nil }
let size = track.naturalSize.applying(track.preferredTransform)
return abs(size.height) / abs(size.width)
}
Is there any way to find the current pdf content size.
For example:-
When user open the pdf file and fill some information in the pdf and click on submit button. Then how to check pdf content size means the content fill fully or not. If user not fully added the details, pending some TextFiled in the pdf, then we need to show alert "Please fill all the information."
Code:-
override func viewDidLoad() {
super.viewDidLoad()
if let documentURL = Bundle.main.url(forResource: pdfName, withExtension: "pdf") {
if let document = PDFDocument(url: documentURL) {
pdfView.autoScales = true
pdfView.backgroundColor = UIColor.lightGray
document.delegate = self
pdfView.document = document
pdfView.autoScales = true
}
}
}
Can someone please explain to me is it possible in swift if yes please guide me how to achieve this, I've tried lot of search but no results yet.
Any help would be greatly appreciated.
Thanks in advance.
I found solution to check PDF Content.
Code:-
func checkPDFTextFiledDataBlankOrNot() {
if let page = pdfVw.document?.page(at: 0){
let annotations = page.annotations
for annotation in annotations{
if annotation.widgetStringValue == " "{
annotation.widgetStringValue = "N/A"
emptyTxtFields += 1
}else {
fillTxtfields += 1
}
}
}
}
I'm trying to get the orientation property of a PDF document. The purpose is, I would like to add a button widget in a location that depends on the orientation of the PDF document.
For example:
func openPDFDocument() {
if let documentURL = Bundle.main.url(forResource: "PDF document", withExtension: "pdf"),
let document = PDFDocument(url: documentURL),
let page = document.page(at: 0) {
// Set our document to the view, center it, and set a background color
pdfView?.document = document
pdfView?.autoScales = true
pdfView?.backgroundColor = UIColor.lightGray
//I think I should be able to add a code here like:
if page.orientation = Horizontal {
self.insertResetButtonInto(page)
} else {
//do nothing or do something else
}
}
}
This is the function I would like to add in case the document is in Landscape mode:
func insertResetButtonInto(_ page: PDFPage) {
let pageBounds = page.bounds(for: .cropBox)
let resetButtonBounds = CGRect(x: 90, y: pageBounds.size.height - 300, width: 106, height: 32)
let resetButton = PDFAnnotation(bounds: resetButtonBounds, forType: PDFAnnotationSubtype(rawValue: PDFAnnotationSubtype.widget.rawValue), withProperties: nil)
resetButton.widgetFieldType = PDFAnnotationWidgetSubtype(rawValue: PDFAnnotationWidgetSubtype.button.rawValue)
resetButton.widgetControlType = .pushButtonControl
resetButton.caption = "Reset"
page.addAnnotation(resetButton)
// Create PDFActionResetForm action to clear form fields.
let resetFormAction = PDFActionResetForm()
resetFormAction.fieldsIncludedAreCleared = false
resetButton.action = resetFormAction
}
I got the example project from Apple's documentation website. I looked at a previous similar question, however it seems this was in Objective C.
I would appreciate the help in this matter.
There is no direct API to get the orientation from a PDFPage. But you can first get the page size from .mediaBox, then calculate the orientation like below.
let pageSize = page.bounds(for: .mediaBox).size
if pageSize.width > pageSize.height {
//landscape
} else {
//portrait
}
I use another way to get the orientation of my pdf page.
func IsLandscape(page: PDFPage) -> Bool {
let pointZero = pdfView.convert(CGPoint(x: 0, y: 0), from: page)
let pointTen = pdfView.convert(CGPoint(x: 10, y: 10), from: page)
let caculate = pointTen.x - pointZero.x
print("pointZero: \(pointZero), pointTen:\(pointTen)")
if (caculate > 0) {
print("landscape")
return true
}
else {
print("portrait")
return false
}
}
I have drawn slots on google maps using Geojson. Now when the user long presses on the slot should ask the user whether to save the location or not. But I don't know how to enable long press only for the slots. Could anybody suggest me how to do this. Below is the code I have used.
`// the below is the code to render the slots on the google maps
let geoJsonParser = GMUGeoJSONParser(url: url!)
geoJsonParser.parse()
let renderer = GMUGeometryRenderer(map: self.mapview, geometries: geoJsonParser.features)
let style = GMUStyle(styleID: "random", stroke: UIColor.black, fill: UIColor.green, width: 2, scale: 1, heading: 0, anchor: CGPoint(x: 0, y: 0), iconUrl: nil, title: nil, hasFill: true, hasStroke: true)
for feature in geoJsonParser.features {
feature.style = style
}
renderer.render()
// The below code is to check whether the user has long pressed on the slot or not(written this code inside the didLongPressAt function).
for feature in geoJsonParser.features {
if GMSGeometryContainsLocation(coordinate, feature as! GMSPath, true) {// the app is crashing with an error in the console "Could not cast value of type 'GMUFeature' (0x1033573d0) to 'GMSPath' (0x1033585a0)"
print("YES: you are in this polygon.")
marker.title = "\(coordinate)"
marker.map = mapview
} else {
print("You do not appear to be in this polygon.")
}
}`
Finally, after lot of efforts I found the solution.
Note: If anyone is integrating Google-Maps-iOS-Utils library, then don't add header files in Bridging-Header file as suggested in Google developer document.
Wherever you are using the below code, just write import GoogleMapsUtils at top.
let path = Bundle.main.path(forResource: "dld_areas", ofType: "json")
let url = URL(fileURLWithPath: path!)
let data: Data = try! Data.init(contentsOf: url)
let json = try JSON(data: data)
//our json is having properties containing area name and area id, bacause the properties field inside the GMUFeature is retuning us nil, we have to pass it indiviually
let jsonFeatures: [JSON] = json["features"].array ?? []
geoJsonParser = GMUGeoJSONParser.init(data: data)
geoJsonParser.parse()
var index = 0
for feature in geoJsonParser.features {
var innerFeature = feature
while(innerFeature.geometry.type != "GeometryCollection"){
print("type is" + innerFeature.geometry.type)
innerFeature = innerFeature.geometry as! GMUGeometryContainer
}
let collection = innerFeature.geometry as! GMUGeometryCollection
let polygon:GMUPolygon = collection.geometries.first as! GMUPolygon
var isAreaObtained = false
for path in polygon.paths{
//check if the coordinates lie within the polygon
if (GMSGeometryContainsLocation(coordinates, path, true)) {
isAreaObtained = true
print(jsonFeatures[index]["properties"]["NAME_EN"])
print(jsonFeatures[index]["properties"]["AREA_ID"])
print("location inside polygon")
break;
}
}
if(isAreaObtained){
break
}
index = index + 1
}
I found the above code in the following link:
https://github.com/googlemaps/google-maps-ios-utils/issues/205
I have used this code and modified as per my requirement.
In my app users will enter information on one screen that gets put into a database. On another screen I want to display a pdf with their information used to fill out the edit text fields (annotations). In the example below the user can click "John Smith" currently and type a new name.
Using ios PDFKit I am able to display the pdf and allow the user to edit. I am unable to update text in the text boxes or change the status of checkboxes. It looks as though I do have read/write privileges for the pdf.
With my current code I was able to get the number of pages of my pdf and print off the annotation names of every annotation so I can get the names of the fields I want to set values for but the setvalue function does not work and checking the contents of a filled textbox shows nil.
override func viewDidLoad() {
super.viewDidLoad()
displayPDF()
updatePDFText()
}
func displayPDF() {
let pdfView = PDFView()
pdfView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(pdfView)
pdfView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
pdfView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
pdfView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
pdfView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
guard let path = Bundle.main.url(forResource: MY_PDF, withExtension: "pdf") else { return }
if let document = PDFDocument(url: path) {
pdfView.document = document
}
}
func updatePDFText() {
guard let path = Bundle.main.url(forResource: MY_PDF, withExtension: "pdf") else { return }
if let document = PDFDocument(url: path) {
for index in 0..<document.pageCount {
if let page = document.page(at: index) {
let annotations = page.annotations
for annotation in annotations {
//print("Annotation Name :: \(annotation.fieldName ?? "")")
if annotation.fieldName == "SSN1" {
annotation.setValue("TEST", forAnnotationKey: .widgetValue)
page.removeAnnotation(annotation)
page.addAnnotation(annotation)
} else if annotation.fieldName == "District" {
annotation.buttonWidgetState = .onState
page.removeAnnotation(annotation)
page.addAnnotation(annotation)
}
}
}
}
}
}
Is there an issue with my implementation or is there another method of programmatically updating the pdf annotations?