I'm using the JavaFx Print-Dialog to customize the print job. All properties will be stored in the PrinterJob#JobSettings variable, but when I receive the paper source from the jobSetting the paper source is always the default.
How can I get the paper source that I set?
Here is a short example:
public class PrinterPaperSourceTest extends Application {
public static void main(String[] args) {
launch( args );
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Printer");
Button btn = new Button();
btn.setText("Show Printer Settings ");
btn.setOnAction( new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
PrinterJob job = PrinterJob.createPrinterJob(Printer.getDefaultPrinter());
job.showPageSetupDialog(null);
Alert alert = new Alert(AlertType.INFORMATION);
PaperSource paperSource = job.getJobSettings().getPaperSource();
alert.setContentText("PaperSource: " + paperSource.getName());
alert.show();
}
});
StackPane root = new StackPane();
root.getChildren().add(btn);
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
}
}
Printing API appeared in fx8.0. And it can print nodes. You can create printer job with javafx.print.PrinterJob class. But it prints only region that fits to a printed page, and not the one that you on a screen. So you need to make your node fit page(scale, translate, etc) by hands.
You can use this code. Hope it will help you.
/**
* Prints the current page displayed within the internal browser, not necessarily the {#link #presentationProperty()}.
* If no printers are installed on the system, an awareness is displayed.
*/
public final void print() {
final PrinterJob job = PrinterJob.createPrinterJob();
if (job != null) {
if (job.showPrintDialog(null)) {
if(this.getPresentation().getArchive() != null) {
final String extension = ".".concat(this.getPresentation().getArchiveExtension());
final int indexOfExtension = this.getPresentation().getArchive().getName().indexOf(extension);
final String jobName = this.getPresentation().getArchive().getName().substring(0, indexOfExtension);
job.getJobSettings().setJobName(jobName);
}
job.getJobSettings().setPrintQuality(PrintQuality.HIGH);
job.getJobSettings().setPageLayout(job.getPrinter().createPageLayout(Paper.A4, PageOrientation.LANDSCAPE, 0, 0, 0, 0));
this.internalBrowser.getEngine().print(job);
job.endJob();
} else {
job.cancelJob();
}
} else {
DialogHelper.showError("No printer", "There is no printer installed on your system.");
}
}
Resource Link:
javafx.print.PrinterJob examples
Introduction by Example: JavaFX 8 Printing
I don’t have an answer but I will try to explain why it happens and why it will not be easy to fix. This behavior seems to be influenced by Internet Printing Protocol (IPP) specifications and is caused by the way IPP is implemented by Java Print Service API (to which JavaFX print jobs delegate). Below is a fragment from Oracle’s tech note that explains limitations of manually setting the paper source (https://docs.oracle.com/javase/8/docs/technotes/guides/jps/spec/attributes.fm5.html):
Media is the IPP attribute that identifies the medium on which to print. The Media attribute is an important attribute to understand, but is relatively complex.
The Java Print Service API defines three subclasses of the abstract class Media to reflect the overloaded Media attribute in the IPP specification: MediaSizeName, MediaName and MediaTray. All the Media subclasses have the Media category, for which each subclass defines different standard attribute values. […]
The value of the Media attribute is always a String, but because the attribute is overloaded, its value determines the type of media to which the attribute refers. For example, the IPP pre-defined set of attribute values include the values "a4" and "top-tray". If Media is set to the value "a4" then the Media attribute refers to the size of paper, but if Media is set to "top-tray" then the Media attribute refers to the paper source. […]
In most cases, applications will use either MediaSizeName or MediaTray. The MediaSizeName class enumerates the media by size. The MediaTray class enumerates the paper trays on a printer, which usually include a main tray and a manual feed tray. The IPP 1.1 specification does not provide for specifying both the media size and the media tray at the same time, which means, for example, that an application cannot request size A4 paper from the manual tray. A future revision of the IPP specification might provide for a way to request more than one type of media at a time, in which case the JPS API will most likely be enhanced to implement this change.
So, MediaTray (or paper source) is not an independent parameter and cannot be set if the Media attribute is already defined by one of the other two ways (MediaSizeName or MediaName). This is exactly what happens with page setup dialogs.
J2DPrinterJob class (from com.sun.prism.j2d.print package) contains the dialog code and updates print job settings (I found this by debugging your application). Below is the method from this class that updates the paper source setting from the dialog.
private void updatePaperSource() {
Media m = (Media)printReqAttrSet.get(Media.class);
if (m instanceof MediaTray) {
PaperSource s = j2dPrinter.getPaperSource((MediaTray)m);
if (s != null) {
settings.setPaperSource(s);
}
}
}
I tested different scenarios and the result was the same: by the time updatePaperSource() starts execution the Media attribute is already defined as MediaSizeName type. So the statements in the if branches are never executed and that’s why the paper source is not updated.
I suspect that paper type or paper size have priority over paper source and because the page setup dialog always defines paper type (there is no ‘Automatic’ option), it overloads the selection of paper source to avoid attribute conflict. This essentially makes this option useless.
It may be a bug in JDK or an intentional design decision. In any case, I don’t see an easy way to solve this problem staying within JavaFX considering that it comes from private methods in Java’s internal API.
After a lot of searching I have found a way to print to a different tray with javafx and this was the first place I looked so I figured here would be the best spot to post my solution it may vary with different tray names mine was Tray 2 it will also print out all trays that are available
private void printImage(Node node) {
PrinterJob job = PrinterJob.createPrinterJob();
if (job != null) {
JobSettings js = job.getJobSettings();
PaperSource papersource = js.getPaperSource();
System.out.println("PaperSource=" + papersource);
PrinterAttributes pa = printer.getPrinterAttributes();
Set<PaperSource> s = pa.getSupportedPaperSources();
System.out.println("# of papersources=" + s.size());
if (s != null) {
for (PaperSource newPaperSource : s) {
System.out.println("newpapersource= " + newPaperSource);
//Here is where you would put the tray name that is appropriate
//in the contains section
if(newPaperSource.toString().contains("Tray 2"))
js.setPaperSource(newPaperSource);
}
}
job.getJobSettings().setJobName("Whatever You want");
ObjectProperty<PaperSource> sources = job.getJobSettings().paperSourceProperty();
System.out.println(sources.toString());
boolean success = job.printPage(node);
if (success) {
System.out.println("PRINTING FINISHED");
job.endJob();
//Stage mainStage = (Stage) root.getScene().getWindow();
//mainStage.close();
}
}
}
Here's My output:
PaperSource=Paper source : Automatic
# of papersources=6
newpapersource= Paper source :
newpapersource= Paper source : Manual Feed in Tray 1
newpapersource= Paper source : Printer auto select
newpapersource= Paper source : Tray 1
newpapersource= Paper source : Tray 2
newpapersource= Paper source : Form-Source
ObjectProperty [bean: Collation = UNCOLLATED
Copies = 1
Sides = ONE_SIDED
JobName = Whatever
Page ranges = null
Print color = COLOR
Print quality = NORMAL
Print resolution = Feed res=600dpi. Cross Feed res=600dpi.
Paper source = Paper source : Tray 2
Page layout = Paper=Paper: Letter size=8.5x11.0 INCH Orient=PORTRAIT leftMargin=54.0 rightMargin=54.0 topMargin=54.0 bottomMargin=54.0, name: paperSource, value: Paper source : Tray 2]
PRINTING FINISHED
Related
I want to search every matched keyword in a pdf file and get their position in the page which they located.
I just found some code in iText5 which looks like match what I need
for (i = 1; i <= pageNum; i++)
{
pdfReaderContentParser.processContent(i, new RenderListener()
{
#Override
public void renderText(TextRenderInfo textRenderInfo)
{
String text = textRenderInfo.getText();
if (null != text && text.contains(KEY_WORD))
{
Float boundingRectange = textRenderInfo
.getBaseline().getBoundingRectange();
resu = new float[3];
System.out.println("======="+text);
System.out.println("h:"+boundingRectange.getHeight());
System.out.println("w:"+boundingRectange.width);
System.out.println("centerX:"+boundingRectange.getCenterX());
System.out.println("centerY:"+boundingRectange.getCenterY());
System.out.println("x:"+boundingRectange.getX());
System.out.println("y:"+boundingRectange.getY());
System.out.println("maxX:"+boundingRectange.getMaxX());
System.out.println("maxY:"+boundingRectange.getMaxY());
System.out.println("minX:"+boundingRectange.getMinX());
System.out.println("minY:"+boundingRectange.getMinY());
resu[0] = boundingRectange.x;
resu[1] = boundingRectange.y;
resu[2] = i;
}
}
#Override
public void renderImage(ImageRenderInfo arg0)
{
}
#Override
public void endTextBlock()
{
}
#Override
public void beginTextBlock()
{
}
});
But I don't know how to deal with it in iText7 .
iText7 has pdf2Data add-on which can easily help you achieving your goal (and help with other data extraction cases).
Let's say you want to extract positions of word Header. We go to https://pdf2data.online demo application, upload our template (any file containing the words you want to extract), and go to data field editor which looks like this:
Now, you can add a data field with a selector that would select the data you are interested in. In this case you can use Regular expression selector which is very flexible generally, but in our case the settings are pretty straightforward:
You can see that the editor application highlights all occurrences of the word we are searching for. Now, let's get back to the first step (there is an icon at the top right of the editor to go back to demo), and download our template (link to the bottom of the icon corresponding to the uploaded file).
Now you can look over the information on how to include pdf2Data in your project at this page: https://pdf2data.online/gettingStarted, roughly the code you need is the following:
LicenseKey.loadLicenseFile("license.xml");
Template template = Pdf2DataExtractor.parseTemplateFromPDF("Template.pdf");
Pdf2DataExtractor extractor = new Pdf2DataExtractor(template);
ParsingResult result = extractor.recognize("toParse.pdf");
for (ResultElement element : result.getResults("Headers")) {
Rectangle bbox = element.getBbox();
int page = element.getPage();
System.out.println(MessageFormat.format("Coordinates on page {0}: [{1}, {2}, {3}, {4}]",
page, bbox.getX(), bbox.getY(), bbox.getX() + bbox.getWidth(), bbox.getY() + bbox.getHeight()));
}
Example output:
Coordinates on page 1: [38.5, 788.346, 77.848, 799.446]
Coordinates on page 1: [123.05, 788.346, 162.398, 799.446]
Coordinates on page 1: [207.6, 788.346, 246.948, 799.446]
Coordinates on page 2: [38.5, 788.346, 77.848, 799.446]
Coordinates on page 2: [123.05, 788.346, 162.398, 799.446]
Coordinates on page 2: [207.6, 788.346, 246.948, 799.446]
pdf2Data add-on is closed source and available only at a commercial license option at the moment. Of course it is possible to port your code directly to iText7 and this would be another solution to the task you have, but I must warn you that your code is not universal for all scenarios, e.g. text in a PDF can be written letter by letter, instead of writing a whole word at once (the visual appearance of the two PDFs can easily stay the same), and in this case the code you attached would not work. pdf2Data handles those cases out of the box, taking the burden out of your shoulders.
I am attempting to send some potentially long text to a printer for... well, printing. Here is a minimal example that demonstrates how I am currently printing text:
#FXML
private void print() {
Text printText = new Text(textArea.getText());
TextFlow printArea = new TextFlow(printText);
printArea.setTextAlignment(TextAlignment.LEFT);
printArea.setMaxHeight(Region.USE_COMPUTED_SIZE);
PrinterJob printerJob = PrinterJob.createPrinterJob();
if (printerJob != null && printerJob.showPrintDialog(textArea.getScene().getWindow())) {
PageLayout pageLayout = printerJob.getJobSettings().getPageLayout();
printArea.setMaxWidth(pageLayout.getPrintableWidth());
if (printerJob.printPage(pageLayout, printArea)) {
printerJob.endJob();
// done printing
} else {
System.err.println("Printing failed!");
}
} else {
System.err.println("Unable to create printer job or printer dialog cancelled by user");
}
}
The code above prints text as expected, except that no matter how much text is being printed, it always prints just a single page. In the print dialog that is shown, under the section for "Print range", I have been selecting the "All" option. I have tried choosing the "Pages" option (which for some reason has been defaulting from 1 to 9999 - this is a little odd since the text should be at most two pages long), but have not had any success in printing more than a single page. I have also tried manually setting the page range on the JobSettings object, but that didn't seem to do anything either.
How can I use the PrinterJob and its related classes to get a long piece of text to properly print on multiple pages?
As far as I know you need to supply multiple Nodes, printAreas in your case, to the PrinterJob, having divided them up beforehand in your code (unfortunately...) Placing your TextFlow in a styled Scene will give you the total size, you can divide that up based on the PageLayout.getPrintableWidth and getPrintableHeight, with the proviso that the user can potentially alter these in the print settings dialog.
job.getJobSettings().setPageRanges(new PageRange(1, numPages));
Tells the job how many pages you can supply. You generally set this before showing the print dialog so that the user can choose how many pages to print, what range etc.
After showing the dialog the JobSettings will be updated with the selected PageRanges the user has chosen, which you then loop over and print individually.
if (job.showPrintDialog(null)) {
JobSettings js = job.getJobSettings();
for (PageRange pr : js.getPageRanges()) {
for (int p = pr.getStartPage(); p <= pr.getEndPage(); p++) {
boolean ok = job.printPage(...code to get your node for the page...);
...take action on success/failure etc.
}
}
}
I have 2 displays connected, so I can either launch my Java application on the primary or the secondary display.
The question is: How can I know which display contains my app window, i.e., is there a way to detect the current display with Java?
java.awt.Window is the base class of all top level windows (Frame, JFrame, Dialog, etc.) and it contains the getGraphicsConfiguration() method that returns the GraphicsConfiguration that window is using. GraphicsConfiguration has the getGraphicsDevice() method which returns the GraphicsDevice that the GraphicsConfiguration belongs to. You can then use the GraphicsEnvironment class to test this against all GraphicsDevices in the system, and see which one the Window belongs to.
Window myWindow = ....
// ...
GraphicsConfiguration config = myWindow.getGraphicsConfiguration();
GraphicsDevice myScreen = config.getDevice();
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
// AFAIK - there are no guarantees that screen devices are in order...
// but they have been on every system I've used.
GraphicsDevice[] allScreens = env.getScreenDevices();
int myScreenIndex = -1;
for (int i = 0; i < allScreens.length; i++) {
if (allScreens[i].equals(myScreen))
{
myScreenIndex = i;
break;
}
}
System.out.println("window is on screen" + myScreenIndex);
The method proposed by Nate does not work when another monitor has just been added to the system and the user repositions the Java window into that monitor. This is a situation my users frequently face, and the only way around it for me has been to restart java.exe to force it to reenumerate the monitors.
The main issue is myWindow.getGraphicsConfiguration().getDevice() always returns the original device where the Java Applet or app was started. You would expect it to show the current monitor, but my own experience (a very time consuming and frustrating one) is that simply relying on myWindow.getGraphicsConfiguration().getDevice() is not foolproof. If someone has a different approach that's more reliable, please let me know.
Performing the match for screens (using the allScreen[i].equals(myScreen) call) then continues to return the original monitor where the Applet was invoked, and not the new monitor where it might have gotten repositioned.
Yes, you can do this with the Window, Frame and Graphics Configuration classes.
See more here:
http://java.sun.com/j2se/1.5.0/docs/api/java/awt/Window.html
http://java.sun.com/j2se/1.5.0/docs/api/java/awt/Frame.html
http://java.sun.com/j2se/1.5.0/docs/api/java/awt/GraphicsConfiguration.html
Nate's solution seem to work in most, but not all cases, as I had to experience. perplexed mentions he had problems when monitors got connected, I had issues with "Win+Left" and "Win+Right" key commands. My solution to the problem looks like this (maybe the solution has problems on it's own, but at least this works better for me than Nate's solution):
GraphicsDevice myDevice = myFrame.getGraphicsConfiguration().getDevice();
for(GraphicsDevice gd:GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()){
if(frame.getLocation().getX() >= gd.getDefaultConfiguration().getBounds().getMinX() &&
frame.getLocation().getX() < gd.getDefaultConfiguration().getBounds().getMaxX() &&
frame.getLocation().getY() >= gd.getDefaultConfiguration().getBounds().getMinY() &&
frame.getLocation().getY() < gd.getDefaultConfiguration().getBounds().getMaxY())
myDevice=gd;
}
This works for me
public static GraphicsDevice getWindowDevice(Window window) {
Rectangle bounds = window.getBounds();
return asList(GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()).stream()
// pick devices where window located
.filter(d -> d.getDefaultConfiguration().getBounds().intersects(bounds))
// sort by biggest intersection square
.sorted((f, s) -> Long.compare(//
square(f.getDefaultConfiguration().getBounds().intersection(bounds)),
square(s.getDefaultConfiguration().getBounds().intersection(bounds))))
// use one with the biggest part of the window
.reduce((f, s) -> s) //
// fallback to default device
.orElse(window.getGraphicsConfiguration().getDevice());
}
public static long square(Rectangle rec) {
return Math.abs(rec.width * rec.height);
}
Slightly different use case: If you want to know the primary display before you create a window somewhere and "display" technically means a java.awt.GraphicsDevice, the corresponding java.awt.GraphicsConfiguration should be
java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration()
A sorted list of GraphicsConfiguration-s is given by
public static GraphicsConfiguration[] getConfigurations()
{
final GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
final GraphicsDevice def = ge.getDefaultScreenDevice();
final List<GraphicsConfiguration> cfgs = new ArrayList<GraphicsConfiguration>();
cfgs.add(def.getDefaultConfiguration());
for (final GraphicsDevice gd : ge.getScreenDevices())
{
if (gd!=def)
{
cfgs.add(gd.getDefaultConfiguration());
}
}
final GraphicsConfiguration[] res = cfgs.toArray(new GraphicsConfiguration[cfgs.size()]);
return res;
}
where the default display is the first in the list.
So I want to println in ACM library dialog with a symbol from a non-English language in Java, but when I run it, only little squares appear.
IODialog dialog = getDialog();
dialog.println("ზაზა");
IODialog uses JOptionPane for its implementation, therefore it is subject to the same unicode handling problems that JOptionPane has.
Here there is a way of overcoming the problem. But we don't like links so let me summarize:
As per the comment above, playing with fonts is what you want to explore. Create a new font like this:
public class MyFont {
/*
Below code I extracted from
http://www.java-forums.org/java-tips/6522-swing-changing-component-default-font.html
then i customized it.
*/
public static void setUIFont (javax.swing.plaf.FontUIResource f){
java.util.Enumeration keys = UIManager.getDefaults().keys();
while (keys.hasMoreElements()) {
Object key = keys.nextElement();
Object value = UIManager.get (key);
if (value instanceof javax.swing.plaf.FontUIResource)
UIManager.put (key, f);
}
}
}
and then you set the actual font with whichever font it is that contains your unicode characters with this line:
MyFont.setUIFont(new javax.swing.plaf.FontUIResource("Iskoola pota",Font.BOLD,18)); // setting the default font for application
So what this does is it changes your default font. There is no more you have to do. If you need to change back to your default font before you made the change, well you have to reset the default font in this way.
For swing applications, default font settings for components are in current theme and can be retrieved using UIManager:
public class JavaTesting {
public static void main(String[] args) {
System.out.println(UIManager.get("Label.font"));
}
}
This can be adjusted in JAVA_HOME/lib/swing.properties for range of applications with for example:
swing.defaultlaf=javax.swing.plaf.nimbus.NimbusLookAndFeel
or set at command line with:
java -Dswing.defaultlaf=javax.swing.plaf.nimbus.NimbusLookAndFeel MyApp
Or the application itself remembers it's look and feel and has this value stored somewhere in configuration (file). It works, because application can set look and feel for itself, e.g.:
UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
This all looks nice, but applications need often smaller fonts (e.g. unimportant status bar message for Label) or larger fonts (Label with some heading). Is there any recommended way for this?
As a user of high density monitor I had to discard lot of java applications, that use code like this - source code copied from squirrel sql:
Font tmp = (Font)UIManager.get("Label.font");
if (tmp != null) {
Font font = tmp.deriveFont(10.0f);
_statusBarFontInfo = new FontInfo(font);
}
This example code makes status bar completely unreadable, but most components are adjusted to new, bigger font.
For creating my own application, I might use some ratio (e.g. 50% to 150% of theme font), but hardcoding 0.50 - 1.50 range looks as a bad coding habbit as well. And it doesn't fix problem with applications that I don't have source code for. This belongs to the theme / L&F (Look & Feel, Look and Feel). Example:
FontUIResource Label_font_resource = (FontUIResource)javax.swing.UIManager.get("Label.font");
Font Label_font = Label_font_resource;
Font Label_font_bigger = Label_font.deriveFont(Label_font.getSize2D() * 1.5f);
It's worth asking before I try some ugly hacks like custom components replacing swing default ones, or Graphics2D.setFont adjustments, or any other scary stuff. :-)
Edit: the only thing that exists, is size variant, which is supported only by Nimbus theme. Allowed values are "mini", "small", "regular", "large". Example:
JComponent mini = new JButton("mini");
mini.putClientProperty("JComponent.sizeVariant", "mini");
However, looking into source code for Nimbus theme, it's not possible to simply adjust these values (e.g. scale to 150% for "large"), it's actually hard-coded!
package javax.swing.plaf.nimbus;
public final class NimbusStyle extends SynthStyle
{
public static final String LARGE_KEY = "large";
public static final String SMALL_KEY = "small";
public static final String MINI_KEY = "mini";
public static final double LARGE_SCALE = 1.15D;
public static final double SMALL_SCALE = 0.857D;
public static final double MINI_SCALE = 0.714D;
This value can be edited only by very advanced user (for example edit constant pool in JAVA_HOME/lib/rt.jar with program called "rej") - i tried it, and it works, BUT it doesn't work always, because they actually hard-coded (!!) the constants at several places (is this really best quality - standard library?), for example:
if ("large".equals(str))
{
this.scrollBarWidth = (int)(this.scrollBarWidth * 1.15D);
this.incrGap = (int)(this.incrGap * 1.15D);
this.decrGap = (int)(this.decrGap * 1.15D);
}
Bottom line: no, Java look and feel doesn't support that for now. I suggest using ratios, e.g. 0.7 - 1.3 of default font size.
JLabel smallLabel = new JLabel("some text");
smallLabel.setFont(smallLabel.getFont().deriveFont(0.8f * smallLabel.getFont().getSize2D()));
Instead of changing the Font, Nimbus has a sizeVariant for mini, small, regular and large components. This article has more examples: Resizing a Component.
myButton.putClientProperty("JComponent.sizeVariant", "mini");
What I've done in my Swing applications is include a Font class. Every other Swing component that needs a font calls the Font class.
import java.awt.Font;
public class MinesweeperFont {
protected static final String FONT_NAME = "Comic Sans MS";
public static Font getBoldFont(int pointSize) {
return new Font(FONT_NAME, Font.BOLD, pointSize);
}
}
Now in this case, I only needed one font type. I let the application pick the font size.
You could define a method for each font size you want, probably calling them small, medium, and large, so you can change the methods later to use a different point size or type if you want.
Since your entire application calls these font static methods, you only have to make font changes in one place.