I am using PDFKit in a Mac app (Xcode 11.7, 10.15 deployment target) to view pdfs. Users are able to highlight selections and either copy the text, or create quotes.
With some pdfs, I cannot get the correct string contents for the highlight.
Take the following pdf: https://www.irs.gov/pub/irs-pdf/iw8bene.pdf. If it is opened in Preview, it is possible to copy and paste contents into TextEdit, for example.
If I open this pdf with PDFView, only some text can be copied and pasted (the main heading for example), but body text only pastes the copied spaces! I have no custom code to handle copy on my PDFView.
If I evaluate the current PDFSelection when the document is highlighted, I get spaces, and nonsense characters in the string:
for character in pdfSelection.string!.unicodeScalars {
print(character.value)
}
Example result:
32
1113109
1113135
1113135
1113109
32
1113118
1113091
32
Whatever is wrong, the standard copy code is falling foul of it too, so perhaps there is some set-up issue on PDFView or PDFDocument that I am missing? I simply create a PDFView in Interface Builder, then open a PDFDocument with a URL and set it on the view.
This issue was being caused elsewhere in my application, but still related to PDFDocument. When dragging a pdf to my app I create a PDFDocument to check validity, then save that item to the app's folder:
guard let pdf = PDFDocument(url: fileURL) else { ... }
guard pdf.write(to: documentURL(forID: documentID, andType: .pdf)) else { ... }
It was this processing of the file that caused it to be subtly modified/broken.
This was naive of me based on the complexity of pdfs. I will simply copy the original file in future.
Related
I have a Word file that I'm manipulating with OpenXML. I have nothing but a picture content control there and I have successfully added a picture ("content") into that picture content control.
Now everything is otherwise fine, but I don't have a slightest clue of how to remove the placeholder picture. That is, I have that "little icon with monitor, sun and mountain" there right in the middle of my inserted picture. You know, the icon that you see when you insert a picture content control. I can take the count of content controls thru VBA and it says there's exactly one, so there's not two controls on top of each other.
If I delete the target content control (with remove-method), the entire content control (including the correctly set picture) gets deleted.
Is there something like "placeholderimage.Hide"-method or something that I should use?
I set the content to the picture like this:
DocumentFormat.OpenXml.Drawing.Blip blip = targetpicturecontrol.Descendants<DocumentFormat.OpenXml.Drawing.Blip>().FirstOrDefault();
blip.Embed = mainPart.GetIdOfPart(imagePart);
This code hides (or removes, whatever) the place holder
private static void RemovePlaceHolder(SdtProperties targetproperties)
{
Appearance appearance = new Appearance();
appearance.Val = new EnumValue<SdtAppearance>();
SdtAppearance sdtAppearance = new SdtAppearance();
sdtAppearance = SdtAppearance.Hidden;
appearance.Val.Value = sdtAppearance;
targetproperties.AppendChild(appearance);
}
You know how sometimes when you close a Finder window or document, it shrinks toward its representation in Finder. I want my application to be able to do this, too. Is there an API for this? I can't find any.
Executive summary: Use the NSDocument system if you want this behavior.
Details:
It looks like you're using TextEdit in your GIF. As it happens, Apple publishes the source code for TextEdit as sample code. So we can look and see if it does anything special to make this happen.
I couldn't find anything in the TextEdit source code. I poked around for a while and set some breakpoints but didn't find any evidence that TextEdit does this “manually”.
I discovered that if you open the file using File > Open (instead of double-clicking the file in the Finder), you do not get the animated closing window, even if the file is visible in the Finder.
But if you open the file using File > Open, then (without closing that window) double-click the file in the Finder, then you do get the animated closing window.
So I poked around a bit more, setting breakpoints and looking at disassembler listings, and I found what I think is the important bits, in -[NSWindow _close]. It goes basically like this:
- (void)_close {
if (!_wFlags.windowDying) { return };
if (_auxiliaryStorage->_auxWFlags.windowClosed) { return; }
void (^actuallyCloseMyself)() = ^{ ... code to actually close the window ... };
NSWindowController *controller = self.windowController;
if (![controller respondsToSelector:#selector(document)]) { goto noCloser; }
NSDocument *document = controller.document;
if (![document respondsToSelector:#selector(fileURL)]) { goto noCloser; }
QLSeamlessDocumentCloser *closer = [[NSDocumentController _seamlessDocumentCloserClass] seamlessDocumentCloserForURL:document.fileURL];
if (closer == nil) { goto noCloser; }
CGRect frame = NSRectZero;
[closer closeWindow:self contentFrame:&frame withBlock:actuallyCloseMyself];
goto done;
noCloser:
actuallyCloseMyself();
done:
_auxiliaryStorage->_auxWFlags.wantsHideOnDeactivate = YES;
}
So basically, if your window is attached to an NSWindowController, and the controller has an NSDocument, then AppKit will try to set up a “seamless close” using a QLSeamlessDocumentCloser. The QL prefix means it's part of QuickLook (the class is in fact found in the QuickLookUI framework, which is part of the Quartz framework).
I guess what happens is, when you open a file in the Finder, the Finder tells the QuickLook system (probably the quicklookd process) where on the screen it is displaying the icon for the file. When the closer is called (in TextEdit), if QuickLook has a frame to close to, it animates the window down to that frame before actually closing the window. If you didn't open the file via the Finder, QuickLook doesn't have a frame to animate to, so it presumably just calls the actuallyCloseMyself block immediately.
Files are not readable in Swift Playground.
How to make files readable?
Same code runs well on Xcode terminal app, but fails on Swift Playground.
Demo code below.
import Foundation
println("Hello, World!")
var fname:String = "/Users/holyfield/Desktop/com.apple.IconComposer.plist"
var fm:NSFileManager = NSFileManager.defaultManager()
if(fm.fileExistsAtPath(fname)){
println("File Exists")
if(fm.isReadableFileAtPath(fname)){
println("File is readable")
var fd:NSData? = NSData(contentsOfFile: fname)
println(fd?.length)
let pl = NSDictionary(contentsOfFile: fname)
println(pl?.count)
println(pl?.allKeys)
}else{
println("File is not readable")
}
}
else{
println("File does not exists")
}
Sample images:
I have to thank Nate Cook for first for his quick response and solid answer.
Just for case I share his answer from another post, which title is misleading.
See Nate's answer here: https://stackoverflow.com/a/26723557/2360439
Playgrounds are sandboxed, so you won't be able to just grab files from anywhere in your user folder. Here's how to add that file to your
playground to make it accessible:
Find your ".playground" file in the Finder Right click and choose "Show Package Contents"
You should see "timeline.xctimeline", "contents.xcplayground", and "section-1.swift"
Make a new folder called "Resources" if it doesn't exists yet.
Copy your files into Resources folder
Seems that there is no way to access files with Swift Playground outside of Playground sandbox. If you know how to access files outside of sandbox, you are welcome to share your solution!!
There are several ways to do load files into a Swift Playground. I'll highlight the Image Literal, File Menu and Context Menu techniques.
Image Literal technique
This is the easiest and COOLEST. You simply drag a file from anywhere on your machine into the code and assign it via a let or var statement. GIF here.
File Menu technique
Choose File -> Add Files to "MyProjectName"
See GIF of this here.
Context Menu technique
You can reveal the current playground in the Xcode (7.3.1) Project Navigator using the right-click menu. Upon revealing the current playground, you'll see a directory entitled Resources. Simply drag the file you want access to into that folder and watch the magic happen (given you've written the file access code).
Example resource loading code
Here's an example of doing this for showing an image:
let url: NSURL! = NSBundle.mainBundle().URLForResource("IMG_4099", withExtension: "JPG")
let imagePath = url.path
let image = UIImage(contentsOfFile: imagePath!)
let imageView = UIImageView.init(image: image)
I've produced an animated GIF that demonstrates this in action. Here's a screengrab from that:
Reading external code
If you want to add auxiliary code in addition to resources, use the Sources folder.
A note regarding the console
The console area of Xcode's Playground interface will show you that the UIImage instance is nil when you load an image file outside the sandbox. If you entered a path you know exists, but the image isn't showing, ensure the UIImage instance isn't printing as nil.
You also can create and use Shared Playground Data folder.
Just like that:
/Users/username/Documents/Shared Playground Data/file.txt
And any file located there becomes readable to any playground!
Files are not readable in Swift Playground.
How to make files readable?
Same code runs well on Xcode terminal app, but fails on Swift Playground.
Demo code below.
import Foundation
println("Hello, World!")
var fname:String = "/Users/holyfield/Desktop/com.apple.IconComposer.plist"
var fm:NSFileManager = NSFileManager.defaultManager()
if(fm.fileExistsAtPath(fname)){
println("File Exists")
if(fm.isReadableFileAtPath(fname)){
println("File is readable")
var fd:NSData? = NSData(contentsOfFile: fname)
println(fd?.length)
let pl = NSDictionary(contentsOfFile: fname)
println(pl?.count)
println(pl?.allKeys)
}else{
println("File is not readable")
}
}
else{
println("File does not exists")
}
Sample images:
I have to thank Nate Cook for first for his quick response and solid answer.
Just for case I share his answer from another post, which title is misleading.
See Nate's answer here: https://stackoverflow.com/a/26723557/2360439
Playgrounds are sandboxed, so you won't be able to just grab files from anywhere in your user folder. Here's how to add that file to your
playground to make it accessible:
Find your ".playground" file in the Finder Right click and choose "Show Package Contents"
You should see "timeline.xctimeline", "contents.xcplayground", and "section-1.swift"
Make a new folder called "Resources" if it doesn't exists yet.
Copy your files into Resources folder
Seems that there is no way to access files with Swift Playground outside of Playground sandbox. If you know how to access files outside of sandbox, you are welcome to share your solution!!
There are several ways to do load files into a Swift Playground. I'll highlight the Image Literal, File Menu and Context Menu techniques.
Image Literal technique
This is the easiest and COOLEST. You simply drag a file from anywhere on your machine into the code and assign it via a let or var statement. GIF here.
File Menu technique
Choose File -> Add Files to "MyProjectName"
See GIF of this here.
Context Menu technique
You can reveal the current playground in the Xcode (7.3.1) Project Navigator using the right-click menu. Upon revealing the current playground, you'll see a directory entitled Resources. Simply drag the file you want access to into that folder and watch the magic happen (given you've written the file access code).
Example resource loading code
Here's an example of doing this for showing an image:
let url: NSURL! = NSBundle.mainBundle().URLForResource("IMG_4099", withExtension: "JPG")
let imagePath = url.path
let image = UIImage(contentsOfFile: imagePath!)
let imageView = UIImageView.init(image: image)
I've produced an animated GIF that demonstrates this in action. Here's a screengrab from that:
Reading external code
If you want to add auxiliary code in addition to resources, use the Sources folder.
A note regarding the console
The console area of Xcode's Playground interface will show you that the UIImage instance is nil when you load an image file outside the sandbox. If you entered a path you know exists, but the image isn't showing, ensure the UIImage instance isn't printing as nil.
You also can create and use Shared Playground Data folder.
Just like that:
/Users/username/Documents/Shared Playground Data/file.txt
And any file located there becomes readable to any playground!
I have been searching for clues on this issue for some time now, with no results. So, here goes...
I have an application that I want to have a simple button to open a file dialog window. There are other buttons on the main window that will read or create/write the file (after doing the appropriate checks for the function selected). I used to use the QFileDialog::getSaveFileName() function without issues, but with Windows 7, this fails if the file exists AND is read-only. I switched to the getOpenFileName() to get around this issue, but now the file dialog fails if the user tries to select a non-existent file (irrelevant on a save operation).
Is there a way to add a "Create New File" icon to the file dialog, or add it to the right-click menu within the file dialog window? I would really hate to have to rewrite the app just because of (yet another) Windows behavior change.
QFileDialog::getOpenFileName() should only be used for opening existing files. If you type in a name of a file that doesn't exist and the system complains, this is proper behaviour. It's correctly telling you that you can't open a file that doesn't exist.
If you want to write to an existing file or create a new file, you should be using QFileDialog::getSaveFileName()
If you're trying to write to an existing file that is marked as Read-Only in the operating system and you get an error saying that the file is Read-Only, then the error is correct. You should not be allowed to write to a read-only file, that's just what Read-Only means.
From what you've explained, there are no errors here. Everything's happening as it should be. If you're trying to force the system to do something different, don't. You should rather try and think of doing things a different way.
Ok, since this was never really answered here, and I have since figured out a solution, I thought I would update this with the code snippet I am using.
void MainWindow::on_tbBrowse_clicked()
{
// Use the location of already entered file
QString fileLocation = leFile->text();
QFileInfo fileinfo(fileLocation);
// See if there is a user-defined file extension.
QString fileType = qgetenv("DiskImagerFiles");
if (fileType.length() && !fileType.endsWith(";;"))
{
fileType.append(";;");
}
fileType.append(tr("Disk Images (*.img *.IMG);;*.*"));
// create a generic FileDialog
QFileDialog dialog(this, tr("Select a disk image"));
dialog.setNameFilter(fileType);
dialog.setFileMode(QFileDialog::AnyFile);
dialog.setViewMode(QFileDialog::Detail);
dialog.setConfirmOverwrite(false);
if (fileinfo.exists())
{
dialog.selectFile(fileLocation);
}
else
{
dialog.setDirectory(myHomeDir);
}
if (dialog.exec())
{
// selectedFiles returns a QStringList - we just want 1 filename,
// so use the zero'th element from that list as the filename
fileLocation = (dialog.selectedFiles())[0];
if (!fileLocation.isNull())
{
leFile->setText(fileLocation);
QFileInfo newFileInfo(fileLocation);
myHomeDir = newFileInfo.absolutePath();
}
setReadWriteButtonState();
updateHashControls();
}
}
setReadWriteButtonState() will enable the buttons according to the file state:
if file is read-only, only Read button is enabled
if file doesn't exist, only Write button is enabled
The entire code is available for others to review at https://sourceforge.net/projects/win32diskimager/. I hope this helps the next person that is looking for a solution to this. Just please include attribution if you use our code.