I have an QTextEdit with some images and i've changed their QTextImageFormat.
But QTextEdit::toHtml() does not export their styles (loading works).
Does anybody have an idea for a workaround?
WebKit is not an option, it's an ancient Project.
It is possible to convert the contents of QTextDocument to another format using QTextBlock. As the official documentation says,
QTextBlock provides read-only access to the block/paragraph structure of QTextDocuments. It is mainly of use <...> if you want to iterate over a document and write out the contents in your own custom format.
The iteration over the contents of QTextEdit's QTextDocument would look like this:
QTextEdit * editor = new QTextEdit;
<...> // fill the editor contents
QTextDocument * doc = editor->document();
for (QTextBlock currentBlock = doc->begin(); currentBlock != doc->end(); currentBlock = currentBlock.next())
{
<...> // optionally process the block itself somehow - look at its QTextBlockFormat, for example
for(QTextBlock::iterator it = currentBlock.begin(); !it.atEnd(); ++it)
{
QTextFragment currentFragment = it.fragment();
if (currentFragment.isValid()) {
processFragment(currentFragment);
}
}
}
See the documentation for QTextFragment to understand how you might implement processFragment from the above example.
On the other hand, you say that WebKit is not an option - OK but there's a replacement for it - Qt WebEngine. I have some experience of implementing a text editor with a few "advanced features" using Qt WebEngine as a backend and I can say it's much harder to achieve than with Qt WebKit but possible.
Related
I would like the JXA equivalent of this AppleScript snippet:
tell application "Finder"
# Get path
set currentTarget to target of window 1
set posixPath to (POSIX path of (currentTarget as alias))
# Show dialog
display dialog posixPath buttons {"OK"}
end tell
The closest I got was using the url property to initialize a Foundation NSURL object and access its fileSystemRepresentation property like so:
// Get path
var finder = Application('Finder')
var currentTarget = finder.finderWindows[0].target()
var fileURLString = currentTarget.url()
// I'd like to get rid of this step
var fileURL = $.NSURL.alloc.initWithString(fileURLString)
var posixPath = fileURL.fileSystemRepresentation
// Show dialog
finder.includeStandardAdditions = true
finder.displayAlert('', {buttons: ['Ok'], message: posixPath})
But this seems unnecessarily complex. Is there a nicer way to get to the POSIX path without using Foundation API or manual string wrangling?
If I naively try this:
finder.finderWindows[0].target().posixPath()
I get this error:
app.startupDisk.folders.byName("Users").folders.byName("kymer").folders.byName("Desktop").posixPath()
--> Error -1728: Can't get object.
This SO answer seems relevant, but I can't seem to adapt it to fit my needs:
App = Application.currentApplication()
App.includeStandardAdditions = true
SystemEvents = Application('System Events')
var pathToMe = App.pathTo(this)
var containerPOSIXPath = SystemEvents.files[pathToMe.toString()].container().posixPath()
Any help would be greatly appreciated!
The fact that such a simple piece of AppleScript code has no straightforward JXA translation is a testament to the sorry state of JXA and macOS automation based on OSA scripting in general:
foo's excellent answer contains helpful background information.
Another pointer is that the last time release notes were published was for OS X 10.11 (El Capitan) (as of this writing, we're on the verge of macOS 10.13 (High Sierra's) release).
This third-party July 2017 blog post announces more broadly that "that question was finally answered at WWDC last month: Apple has abandoned its automation technologies, leaving them to wither and die."
As your own example suggests, among the two dying automation scripting languages AppleScript - despite all its warts - is the more mature, reliable choice.
To solve your problem in JXA, it looks like you've come up with the best approach yourself.
Let me package it as a helper function that perhaps easies the pain somewhat - to be clear: such a helper function should NOT be necessary:
// Helper function: Given a Finder window, returns its folder's POSIX path.
// Note: No need for an ObjC.import() statement, because NSURL is
// a Foundation class, and all Foundation classes are implicitly
// available.
function posixPath(finderWin) {
return $.NSURL.alloc.initWithString(finderWin.target.url()).fileSystemRepresentation
}
// Get POSIX path of Finder's frontmost window:
posixPath(Application('Finder').finderWindows[0])
In theory you'd write something like:
finder.finderWindows[0].target({as:"alias"})
but this doesn't work and there's nothing in the documentation to indicate it's supported. But this is SOP for JXA which, like Apple's earlier Scripting Bridge, suffers numerous design flaws and omissions, which never have (and likely never will be) fixed.[1]
FWIW, here's how you do it in Node.js, using NodeAutomation:
$ node
> Object.assign(this,require('nodeautomation'));undefined
> const fn = app('Finder')
> var file = fn.FinderWindows[0].target({asType:k.alias}) // returns File object
> file.toString() // converts File object to POSIX path string
'/Users/jsmith/dev/nodeautomation'
(Be aware that NodeAutomation is a very low-priority project for me, given that Mac Automation looks to be pretty much on its last legs at Apple. Caveat emptor, etc. For non-trivial scripting I strongly recommend sticking to AppleScript as it's the only officially supported solution that actually works right.)
[1] For instance, another JXA limitation is that most apps' move and duplicate commands are seriously crippled cos the JXA authors forgot to implement insertion reference forms. (BTW, I reported all these problems before JXA was even released, and appscript had all this stuff solved a decade ago, so they've no excuse for not getting it right either.)
#Kymer, you said:
But this seems unnecessarily complex. Is there a nicer way to get to
the POSIX path without using Cocoa API or manual string wrangling?
You're on the right track. Here's the best method I know of. If there are better, I too would like to know about them. But, this seems to work well as fast, and works for both files and folders.
var finderApp = Application("Finder");
var itemList = finderApp.selection();
var oItem = itemList[0];
var oItemPaths = getPathInfo(oItem);
/* --- oItemPaths Object Keys ---
oItemPaths.itemClass
oItemPaths.fullPath
oItemPaths.parentPath
oItemPaths.itemName
*/
console.log(JSON.stringify(oItemPaths, undefined, 4))
function getPathInfo(pFinderItem) {
var itemClass = pFinderItem.class(); // returns "folder" if item is a folder.
var itemURL = pFinderItem.url();
var fullPath = decodeURI(itemURL).slice(7);
//--- Remove Trailing "/", if any, to handle folder item ---
var pathElem = fullPath.replace(/\/$/,"").split('/')
var itemName = pathElem.pop();
var parentPath = pathElem.join('/');
return {
itemClass: itemClass,
fullPath: fullPath,
parentPath: parentPath,
itemName: itemName
};
}
Here's a fairly simple example function that just grabs the window's target and then strips off the leading file:// from its url.
/*
pathToFrontWindow()
returns path to front Finder window
*/
function pathToFrontWindow() {
if ( finder.windows.length ) {
return decodeURI( finder.windows[0].target().url().slice(7) )
} else {
return ""
}
}
(() => {
// getFinderDirectory :: () -> String
const getFinderDirectory = () =>
Application('Finder')
.insertionLocation()
.url()
.slice(7);
return getFinderDirectory();
})();
I know how to use VS Extensibility to get the entire active document's text. Unfortunately, that only gets me the text and doesn't give me the formatting, and I want that too.
I can, for example, get an IWpfTextView but once I get it, I'm not sure what to do with it. Are there examples of actually getting all the formatting from it? I'm only really interested in text foreground/background color, that's it.
Note: I need the formatted text on every edit, so unfortunately doing cut-and-paste using the clipboard is not an option.
Possibly the simplest method is to select all of the text and copy it to the clipboard. VS puts the rich text into the clipboard, so when you paste, elsewhere, you'll get the colors (assuming you handle rich text in your destination).
Here's my not-the-simplest solution. TL;DR: you can jump to the code at https://github.com/jimmylewis/GetVSTextViewFormattedTextSample.
The VS editor uses "classifications" to show segments of text which have special meaning. These classifications can then be formatted differently according to the language and user settings.
There's an API for getting the classifications in a document, but it didn't work for me. Or other people, apparently. But we can still get the classifications through an ITagAggregator<IClassificationTag>, as described in the preceding link, or right here:
[Import]
IViewTagAggregatorFactoryService tagAggregatorFactory = null;
// in some method...
var classificationAggregator = tagAggregatorFactory.CreateTagAggregator<IClassificationTag>(textView);
var wholeBufferSpan = new SnapshotSpan(textBuffer.CurrentSnapshot, 0, textBuffer.CurrentSnapshot.Length);
var tags = classificationAggregator.GetTags(wholeBufferSpan);
Armed with these, we can rebuild the document. It's important to note that some text is not classified, so you have to piece everything together in chunks.
It's also notable that at this point, we have no idea how any of these tags are formatted - i.e. the colors used during rendering. If you want to, you can define your own mapping from IClassificationType to a color of your choice. Or, we can ask VS for what it would do using an IClassificationFormatMap. Again, remember, this is affected by user settings, Light vs. Dark theme, etc.
Either way, it could look something like this:
// Magic sauce pt1: See the example repo for an RTFStringBuilder I threw together.
RTFStringBuilder sb = new RTFStringBuilder();
var wholeBufferSpan = new SnapshotSpan(textBuffer.CurrentSnapshot, 0, textBuffer.CurrentSnapshot.Length);
// Magic sauce pt2: see the example repo, but it's basically just
// mapping the spans from the snippet above with the formatting settings
// from the IClassificationFormatMap.
var textSpans = GetTextSpansWithFormatting(textBuffer);
int currentPos = 0;
var formattedSpanEnumerator = textSpans.GetEnumerator();
while (currentPos < wholeBufferSpan.Length && formattedSpanEnumerator.MoveNext())
{
var spanToFormat = formattedSpanEnumerator.Current;
if (currentPos < spanToFormat.Span.Start)
{
int unformattedLength = spanToFormat.Span.Start - currentPos;
SnapshotSpan unformattedSpan = new SnapshotSpan(textBuffer.CurrentSnapshot, currentPos, unformattedLength);
sb.AppendText(unformattedSpan.GetText(), System.Drawing.Color.Black);
}
System.Drawing.Color textColor = GetTextColor(spanToFormat.Formatting.ForegroundBrush);
sb.AppendText(spanToFormat.Span.GetText(), textColor);
currentPos = spanToFormat.Span.End;
}
if (currentPos < wholeBufferSpan.Length)
{
// append any remaining unformatted text
SnapshotSpan unformattedSpan = new SnapshotSpan(textBuffer.CurrentSnapshot, currentPos, wholeBufferSpan.Length - currentPos);
sb.AppendText(unformattedSpan.GetText(), System.Drawing.Color.Black);
}
return sb.ToString();
Hope this helps with whatever you're doing. The example repo will ask if you you want the formatted text in the clipboard after each edit, but that was just a dirty way that I could test and see that it worked. It's annoying, but it was just a PoC.
I've found the beautify extension in Ace editor but I don't see any examples of how to use it. Is there a way to set any options?
Example what I have so far:
var beautiful = ace.require("ace/ext/beautify"); // get extension
var editor = ace.edit("editor"); // reference to our editor
editor.setValue(someCode); // add some code to the editor
beautiful.beautify(editor.session); // beautify the code
When I call this method the code is formatted but it is all unindented / outdented all the way to the left and some spaces are removed. It doesn't look quite right. So I want to know if there are any options. I looked at the code but it is minified. Which is why I'm asking this question here.
Before call:
After call:
UPDATE:
I found an unminifed copy of the extension. It looks like there are no options and it looks like it only works for PHP:
exports.beautify = function(session) {
var iterator = new TokenIterator(session, 0, 0);
var token = iterator.getCurrentToken();
var context = session.$modeId.split("/").pop();
var code = phpTransform(iterator, context);
session.doc.setValue(code);
};
I did not find any options in the beautify.js code for the beautify() method. I found a comment saying that it is not being worked on or supported any more. I do not have the source but it said it was not working and was then abandoned.
It might work alright for JavaScript but is not working well for ActionScript.
I use different tools like processing to create vector plots. These plots are written as single or multi-page pdfs. I would like to include these plots in a single report-like pdf using pdfbox.
My current workflow includes these pdfs as images with the following pseudo code
PDDocument inFile = PDDocument.load(file);
PDPage firstPage = (PDPage) inFile.getDocumentCatalog().getAllPages().get(0);
BufferedImage image = firstPage.convertToImage(BufferedImage.TYPE_INT_RGB, 300);
PDXObjectImage ximage = new PDPixelMap(document, image);
PDPageContentStream contentStream = new PDPageContentStream(document, page);
contentStream.drawXObject(ximage, 0, 0, ximage.getWidth(), ximage.getHeight());
contentStream.close();
While this works it looses the benefits of the vector file formats, espectially file/size vs. printing qualitity.
Is it possible to use pdfbox to include other pdf pages as embedded objects within a page (Not added as a separate page)? Could I e.g. use a PDStream? I would prefer a solution like pdflatex is able to embed pdf figures into a new pdf document.
What other Java libraries can you recommend for that task?
Is it possible to use pdfbox to include other pdf pages as embedded objects within a page
It should be possible. The PDF format allows the use of so called form xobjects to serve as such embedded objects. I don't see an explicit implementation for that, though, but the procedure is similar enough to what PageExtractor or PDFMergerUtility do.
A proof of concept derived from PageExtractor using the current SNAPSHOT of the PDFBox 2.0.0 development version:
PDDocument source = PDDocument.loadNonSeq(SOURCE, null);
List<PDPage> pages = source.getDocumentCatalog().getAllPages();
PDDocument target = new PDDocument();
PDPage page = new PDPage();
PDRectangle cropBox = page.findCropBox();
page.setResources(new PDResources());
target.addPage(page);
PDFormXObject xobject = importAsXObject(target, pages.get(0));
page.getResources().addXObject(xobject, "X");
PDPageContentStream content = new PDPageContentStream(target, page);
AffineTransform transform = new AffineTransform(0, 0.5, -0.5, 0, cropBox.getWidth(), 0);
content.drawXObject(xobject, transform);
transform = new AffineTransform(0.5, 0.5, -0.5, 0.5, 0.5 * cropBox.getWidth(), 0.2 * cropBox.getHeight());
content.drawXObject(xobject, transform);
content.close();
target.save(TARGET);
target.close();
source.close();
This code imports the first page of a source document to a target document as XObject and puts it twice onto a page there with different scaling and rotation transformations, e.g. for this source
it creates this
The helper method importAsXObject actually doing the import is defined like this:
PDFormXObject importAsXObject(PDDocument target, PDPage page) throws IOException
{
final PDStream src = page.getContents();
if (src != null)
{
final PDFormXObject xobject = new PDFormXObject(target);
OutputStream os = xobject.getPDStream().createOutputStream();
InputStream is = src.createInputStream();
try
{
IOUtils.copy(is, os);
}
finally
{
IOUtils.closeQuietly(is);
IOUtils.closeQuietly(os);
}
xobject.setResources(page.findResources());
xobject.setBBox(page.findCropBox());
return xobject;
}
return null;
}
As mentioned above this is only a proof of concept, corner cases have not yet been taken into account.
To update this question:
There is already a helper class in org.apache.pdfbox.multipdf.LayerUtility to do the import.
Example to show superimposing a PDF page onto another PDF: SuperimposePage.
This class is part of the Apache PDFBox Examples and sample transformations as shown by #mkl were added to it.
As mkl appropriately suggested, PDFClown is among the Java libraries which provide explicit support for page embedding (so-called Form XObjects (see PDF Reference 1.7, ยง 4.9)).
In order to let you get a taste of the way PDFClown works, the following code represents the equivalent of mkl's PDFBox solution (NOTE: as mkl later stated, his code sample was by no means optimised, so this comparison may not correspond to the actual status of PDFBox -- comments are welcome to clarify this):
Document source = new File(SOURCE).getDocument();
Pages sourcePages = source.getPages();
Document target = new File().getDocument();
Page targetPage = new Page(target);
target.getPages().add(targetPage);
XObject xobject = sourcePages.get(0).toXObject(target);
PrimitiveComposer composer = new PrimitiveComposer(targetPage);
Dimension2D targetSize = targetPage.getSize();
Dimension2D sourceSize = xobject.getSize();
composer.showXObject(xobject, new Point2D.Double(targetSize.getWidth() * .5, targetSize.getHeight() * .35), new Dimension(sourceSize.getWidth() * .6, sourceSize.getHeight() * .6), XAlignmentEnum.Center, YAlignmentEnum.Middle, 45);
composer.showXObject(xobject, new Point2D.Double(targetSize.getWidth() * .35, targetSize.getHeight()), new Dimension(sourceSize.getWidth() * .4, sourceSize.getHeight() * .4), XAlignmentEnum.Left, YAlignmentEnum.Top, 90);
composer.flush();
target.getFile().save(TARGET, SerializationModeEnum.Standard);
source.getFile().close();
Comparing this code to PDFBox's equivalent you can notice some relevant differences which show PDFClown's neater style (it would be nice if some PDFBox expert could validate my assertions):
Page-to-FormXObject conversion: PDFClown natively supports a dedicated method (Page.toXObject()), so there's no need for additional heavy-lifting such as the helper method importAsXObject();
Resource management: PDFClown automatically (and transparently) allocates page resources, so there's no need for explicit calls such as page.getResources().addXObject(xobject, "X");
XObject drawing: PDFClown supports both high-level (explicit scale, translation and rotation anchors) and low-level (affine transformations) methods to place your FormXObject into the page, so there's no need to necessarily deal with affine transformations.
The whole point is that PDFClown features a rich architecture made up of multiple abstraction layers: according to your requirements, you can choose the most appropriate coding style (either to delve into PDF's low-level basic structures or to leverage its convenient and elegant high-level model). PDFClown lets you tweak every single byte and solve complex tasks with a ridiculously simple method call, at your will.
DISCLOSURE: I'm the lead developer of PDFClown.
How can I change color of lines in Visual Studio 2010 based on some custom pattern? For example, I want to change color of all lines that start with logger. . Is it possible at all?
I have ReSharper 5 installed too.
I wrote up a quick little extension to do this; since you'll very likely want to modify it, you should grab the source. The important part is the code in LayoutChanged:
void ViewLayoutChanged(object sender, TextViewLayoutChangedEventArgs e)
{
IWpfTextView view = sender as IWpfTextView;
var adornmentLayer = view.GetAdornmentLayer("HighlightLines");
foreach (var line in e.NewOrReformattedLines)
{
if (line.Extent.GetText().StartsWith("logger.", StringComparison.OrdinalIgnoreCase))
{
Rectangle rect = new Rectangle()
{
Width = view.ViewportWidth + view.MaxTextRightCoordinate,
Height = line.Height,
Fill = Brushes.AliceBlue
};
Canvas.SetTop(rect, line.Top);
Canvas.SetLeft(rect, 0);
adornmentLayer.AddAdornment(line.Extent, null, rect);
}
}
}
To build/run this, you'll need to:
Download the VS2010 SDK.
Create a new project from the editor extension templates (I usually pick Visual C# -> Extensibility -> Editor Text Adornment).
Delete all the source files it creates.
Add HighlightMatchingLines.cs to the project.
F5 to run/test.
If you want to change the brush, change the Fill = Brushes.AliceBlue line.
If you want to change what text is matched, changed the condition in the if expression.
If you want to change what file type the extension is loaded for, change the [ContentType] attribute. The "Content Types" section of this msdn page lists out some of the common ones.