विंडोज़ पर JavaFX में ब्लिंगरी ने स्विंगिंगकोड प्रस्तुत किया

Aug 17 2020

अवलोकन

विभिन्न कारणों से WebView से बचने के लिए, JavaFX एप्लिकेशन के भीतर फ़्लाइंगसाउज़र का उपयोग करना:

  • सिंक्रोनस व्यवहार के लिए अपने स्क्रॉलबार पर सीधे एपीआई पहुंच प्रदान नहीं करता है;
  • बंडलों जावास्क्रिप्ट, जो मेरे उपयोग के मामले के लिए एक विशाल ब्लोट है; तथा
  • विंडोज पर चलने में विफल।

फ्लाइंगसाउटर स्विंग का उपयोग करता है, जिसे जावाएफएक्स के साथ-साथ उपयोग करने के लिए अपने XHTMLPanel(एक उपवर्ग JPanel) को लपेटने की आवश्यकता होती है SwingNode। सब कुछ बहुत अच्छा काम करता है, आवेदन वास्तविक समय में मार्कडाउन को प्रस्तुत करता है, और उत्तरदायी है। यहां लिनक्स पर चल रहे एप्लिकेशन का डेमो वीडियो है।

मुसीबत

विंडोज पर टेक्स्ट रेंडरिंग धुंधली है। जब एक में चल रहा है, एक से JFrameलिपटे नहीं SwingNode, लेकिन फिर भी वीडियो में दिखाए गए एक ही एप्लिकेशन का हिस्सा है, पाठ की गुणवत्ता निर्दोष है। स्क्रीन कैप्चर एप्लिकेशन की मुख्य विंडो (नीचे) दिखाता है, जिसमें SwingNodeउपरोक्त JFrame(शीर्ष) के साथ शामिल है । आपको "l" या "k" के सीधे किनारे पर ज़ूम करना पड़ सकता है, यह देखने के लिए कि एक तेज और दूसरा धुंधला क्यों है:

यह केवल विंडोज पर होता है। सिस्टम के फॉन्ट प्रीव्यू प्रोग्राम के जरिए विंडोज पर फॉन्ट को देखने पर फोंट एलसीडी स्क्रीन के इस्तेमाल से एंटीआॅलिड होते हैं। एप्लिकेशन ग्रेस्केल का उपयोग करता है। मुझे संदेह है कि अगर ग्रेस्केल के बजाय एंटीएलियासिंग के लिए रंग का उपयोग करने के लिए मजबूर करने का एक तरीका है, तो समस्या गायब हो सकती है। फिर, जब अपने आप में चल रहा है, तो JFrameकोई समस्या नहीं है और एलसीडी रंगों का उपयोग नहीं किया जाता है।

कोड

यहाँ उस कोड के लिए JFrameएक सही प्रस्तुत करना है:

  private static class Flawless {
    private final XHTMLPanel panel = new XHTMLPanel();
    private final JFrame frame = new JFrame( "Single Page Demo" );

    private Flawless() {
      frame.getContentPane().add( new JScrollPane( panel ) );
      frame.pack();
      frame.setSize( 1024, 768 );
    }

    private void update( final org.w3c.dom.Document html ) {
      frame.setVisible( true );

      try {
        panel.setDocument( html );
      } catch( Exception ignored ) {
      }
    }
  }

धुंधली के लिए कोड SwingNodeथोड़ा अधिक शामिल है ( पूरी सूची देखें ), लेकिन यहां कुछ प्रासंगिक स्निपेट हैं (ध्यान दें कि अपडेट के दौरान केवल कुछ अवांछित ऑटोस्कोपिंग को दबाने के लिए इसका HTMLPanelविस्तार होता है XHTMLPanel):

private final HTMLPanel mHtmlRenderer = new HTMLPanel();
private final SwingNode mSwingNode = new SwingNode();
private final JScrollPane mScrollPane = new JScrollPane( mHtmlRenderer );

// ...

final var context = getSharedContext();
final var textRenderer = context.getTextRenderer();
textRenderer.setSmoothingThreshold( 0 );

mSwingNode.setContent( mScrollPane );

// ...

// The "preview pane" contains the SwingNode.
final SplitPane splitPane = new SplitPane(
    getDefinitionPane().getNode(),
    getFileEditorPane().getNode(),
    getPreviewPane().getNode() );

न्यूनतम कार्य उदाहरण

यहाँ एक न्यूनतम स्व-निहित उदाहरण है:

import javafx.application.Application;
import javafx.application.Platform;
import javafx.embed.swing.SwingNode;
import javafx.scene.Scene;
import javafx.scene.control.SplitPane;
import javafx.stage.Stage;
import org.jsoup.Jsoup;
import org.jsoup.helper.W3CDom;
import org.xhtmlrenderer.simple.XHTMLPanel;

import javax.swing.*;

import static javax.swing.SwingUtilities.invokeLater;
import static javax.swing.UIManager.getSystemLookAndFeelClassName;
import static javax.swing.UIManager.setLookAndFeel;

public class FlyingSourceTest extends Application {
  private final static String HTML = "<!DOCTYPE html><html><head" +
      "><style type='text/css'>body{font-family:serif; background-color: " +
      "#fff; color:#454545;}</style></head><body><p style=\"font-size: " +
      "300px\">TEST</p></body></html>";

  public static void main( String[] args ) {
    Application.launch( args );
  }

  @Override
  public void start( Stage primaryStage ) {
    invokeLater( () -> {
      try {
        setLookAndFeel( getSystemLookAndFeelClassName() );
      } catch( Exception ignored ) {
      }

      primaryStage.setTitle( "Hello World!" );

      final var renderer = new XHTMLPanel();
      renderer.getSharedContext().getTextRenderer().setSmoothingThreshold( 0 );
      renderer.setDocument( new W3CDom().fromJsoup( Jsoup.parse( HTML ) ) );

      final var swingNode = new SwingNode();
      swingNode.setContent( new JScrollPane( renderer ) );

      final var root = new SplitPane( swingNode, swingNode );

      // ----------
      // Here be dragons? Using a StackPane, instead of a SplitPane, works.
      // ----------
      //StackPane root = new StackPane();
      //root.getChildren().add( mSwingNode );

      Platform.runLater( () -> {
        primaryStage.setScene( new Scene( root, 300, 250 ) );
        primaryStage.show();
      } );
    } );
  }
}

न्यूनतम काम करने के उदाहरण से धुंधला हो जाना; खुलासा पत्र किनारों में ज़ूम करना तेज विरोधाभासों के बजाय भारी विरोधी हैं:

JLabelयह भी एक ही फजी रेंडर प्रदर्शित करता है:

  final var label = new JLabel( "TEST" );
  label.setFont( label.getFont().deriveFont( Font.BOLD, 128f ) );

  final var swingNode = new SwingNode();
  swingNode.setContent( label );

प्रयास

यहाँ अधिकांश तरीके हैं जिनसे मैंने धब्बा हटाने की कोशिश की है।

जावा

जावा पक्ष में, किसी ने आवेदन का उपयोग करने का सुझाव दिया :

-Dawt.useSystemAAFontSettings=off
-Dswing.aatext=false

पाठ के किसी भी संकेत ने मदद नहीं की है।

SwingNodeभीतर की सामग्री सेट करने SwingUtilities.invokeLaterका कोई प्रभाव नहीं है।

JavaFX

किसी और ने उल्लेख किया कि कैशिंग बंद करने से मदद मिली, लेकिन यह जावाएफ़एक्स के लिए था ScrollPane, न कि किसी जावा के भीतर SwingNode। यह काम नहीं किया।

इसमें JScrollPaneनिहित SwingNodeएलाइनमेंट एक्स और एलाइनमेंट वाई सेट क्रमशः 0.5 और 0.5 है। आधे-पिक्सेल की ऑफसेट सुनिश्चित करना कहीं और अनुशंसित है । मैं कल्पना नहीं कर सकता कि Sceneउपयोग करने के लिए सेटिंग करने से StrokeType.INSIDEकोई फ़र्क पड़ेगा, हालाँकि मैंने बिना किसी लाभ के 1 की चौड़ाई का उपयोग करके कोशिश की थी।

उड़न तश्तरी

फ्लाइंगसाउसर में कई कॉन्फ़िगरेशन विकल्प हैं । सेटिंग्स के विभिन्न संयोजनों में शामिल हैं:

java -Dxr.text.fractional-font-metrics=true \
     -Dxr.text.aa-smoothing-level=0 \
     -Dxr.image.render-quality=java.awt.RenderingHints.VALUE_INTERPOLATION_BICUBIC
     -Dxr.image.scale=HIGH \
     -Dxr.text.aa-rendering-hint=VALUE_TEXT_ANTIALIAS_GASP -jar ...

xr.image.सेटिंग्स केवल बल्कि कैसे FlyingSaucer से उत्पादन के भीतर JavaFX द्वारा प्रदान की गई है की तुलना में FlyingSaucer द्वारा प्रदान की गई छवियों को प्रभावित, SwingNode

सीएसएस फ़ॉन्ट आकार के लिए अंक का उपयोग करता है ।

अनुसंधान

  • https://stackoverflow.com/a/26227562/59087 - ऐसा लगता है कि कुछ समाधान मददगार हो सकते हैं।
  • https://bugs.openjdk.java.net/browse/JDK-8089499- यह प्रयोग कर रहा है SwingNodeऔर क्योंकि लागू करने के लिए प्रतीत नहीं होता है JScrollPane
  • https://stackoverflow.com/a/24124020/59087 - शायद प्रासंगिक नहीं है क्योंकि उपयोग में कोई XML दृश्य बिल्डर नहीं है।
  • https://www.cs.mcgill.ca/media/tech_reports/42_Lessons_Learned_in_Migrating_from_Swing_to_JavaFX_LzXl9Xv.pdf - पेज 8 में 0.5 पिक्सलों द्वारा शिफ्टिंग का वर्णन है, लेकिन कैसे?
  • https://dlsc.com/2014/07/17/javafx-tip-9-do-not-mix-swing-javafx/ - जावाएफ़एक्स और स्विंग का मिश्रण नहीं करने का सुझाव देता है, लेकिन शुद्ध स्विंग के लिए जाना एक विकल्प नहीं है: मैं जल्द ही दूसरी भाषा में ऐप को फिर से लिखूंगा।

OpenJDK / JavaFX के खिलाफ बग के रूप में स्वीकार किया गया:

  • https://bugs.openjdk.java.net/browse/JDK-8252255

JDK और JRE

JavaFX बंडल के साथ बेल्सॉफ्ट के OpenJDK का उपयोग करना । मेरी जानकारी के लिए, OpenJDK को अभी कुछ समय के लिए Freetype समर्थन मिला है। इसके अलावा, फॉन्ट लिनक्स पर बहुत अच्छा लगता है, इसलिए यह JDK नहीं है।

स्क्रीन

निम्न स्क्रीन विनिर्देश समस्या को प्रदर्शित करते हैं, लेकिन अन्य लोगों (विभिन्न मॉनिटर और संकल्पों को देखकर, निस्संदेह) ने इस मुद्दे का उल्लेख किया है।

  • 15.6 "4: 3 एचडी (1366x768)
  • पूर्ण HD (1920x1080)
  • वाइड व्यू एंगल एलईडी बैकलाइट
  • ASUS n56v

सवाल

XHTMLPanelजब फ्लाइंगसाउज़र SwingNodeविंडोज में धुंधली हो जाती है , और अभी तक उसी JavaFX एप्लिकेशन में रनिंग में उसी XHTMLPanelको प्रदर्शित करता JFrameहै तो वह कुरकुरा दिखाई देता है? समस्या को कैसे ठीक किया जा सकता है?

समस्या शामिल है SplitPane

जवाब

1 mipa Aug 19 2020 at 19:17

कुछ विकल्प हैं जो आप कोशिश कर सकते हैं, हालांकि मुझे यह स्वीकार करना होगा कि मैं फ्लाइंगसाउटर और इसके एपीआई को नहीं जानता हूं।

फ्लाइंगसौसर के अलग-अलग रेंडर हैं। इस प्रकार यह संभव है कि इस लाइब्रेरी का उपयोग करके स्विंग / एडब्ल्यूटी रेंडरिंग से पूरी तरह से बचा जा सके, ताकि सीधे सभी एक्सएफएक्स में सभी रेंडरिंग कर सकें।https://github.com/jfree/fxgraphics2d

एक और संभावना है कि फ्लाइंगसैसर को एक छवि में प्रस्तुत किया जाए जो कि सीधी बफ़र्स के माध्यम से जावाएफएक्स में बहुत कुशलता से प्रदर्शित किया जा सकता है। मेरे भंडार में AWTImage कोड यहाँ देखें:https://github.com/mipastgt/JFXToolsAndDemos

1 weisj Aug 20 2020 at 01:48

मैं स्वयं इस मुद्दे को पुन: पेश करने में सक्षम नहीं था, इसलिए आपके द्वारा उपयोग किए जा रहे JDK / JavaFX संस्करण के संयोजन में कुछ समस्या हो सकती है। यह भी संभव है कि मुद्दा केवल प्रदर्शन आकार और स्क्रीन स्केलिंग के विशिष्ट संयोजन के साथ उठता है।

मेरा सेटअप निम्नलिखित है:

  • JavaFX 14
  • OpenJDK 14
import javafx.application.Application;
import javafx.application.Platform;
import javafx.embed.swing.SwingNode;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import org.jsoup.Jsoup;
import org.jsoup.helper.W3CDom;
import org.jsoup.nodes.Document;
import org.xhtmlrenderer.simple.XHTMLPanel;

import javax.swing.*;

public class FlyingSourceTest extends Application {

    private final static String HTML_PREFIX = "<!DOCTYPE html>\n"
            + "<html>\n"
            + "<body>\n";
    private static final String HTML_CONTENT =
            "<p style=\"font-size:500px\">TEST</p>";
    private final static String HTML_SUFFIX = "<p style='height=2em'>&nbsp;</p></body></html>";

    public static void main(String[] args) {
        Application.launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        SwingUtilities.invokeLater(() -> {
            try {
                UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) {
                e.printStackTrace();
            }
            primaryStage.setTitle("Hello World!");

            XHTMLPanel mHtmlRenderer = new XHTMLPanel();
            mHtmlRenderer.getSharedContext().getTextRenderer().setSmoothingThreshold(0);
            SwingNode mSwingNode = new SwingNode();
            JScrollPane mScrollPane = new JScrollPane(mHtmlRenderer);

            String htmlContent = HTML_PREFIX + HTML_CONTENT + HTML_SUFFIX;
            Document jsoupDoc = Jsoup.parse(htmlContent);
            org.w3c.dom.Document w3cDoc = new W3CDom().fromJsoup(jsoupDoc);

            mHtmlRenderer.setDocument(w3cDoc);

            mSwingNode.setContent(mScrollPane);
//            AnchorPane anchorPane = new AnchorPane();
//            anchorPane.getChildren().add(mSwingNode);
//            AnchorPane.setTopAnchor(mSwingNode, 0.5);
//            AnchorPane.setLeftAnchor(mSwingNode, 0.5);
//            mSwingNode.setTranslateX(0.5);
//            mSwingNode.setTranslateY(0.5);

            StackPane root = new StackPane();
            root.getChildren().add(mSwingNode);
            Platform.runLater(() -> {
                primaryStage.setScene(new Scene(root, 300, 250));
                primaryStage.show();
            });
        });
    }
}
DaveJarvis Aug 27 2020 at 01:33

इस मुद्दे को OpenJDK / JavaFX के खिलाफ बग के रूप में स्वीकार किया गया है:

  • https://bugs.openjdk.java.net/browse/JDK-8252255

Mipa के सुझावों में से कोई भी व्यवहार में काम नहीं करेगा। फ्लाइंगसाउटर कसकर एक के साथ एकीकृत होता है JScrollPane, जो फ्लाइंगसाउज़र को जावाएफएक्स-आधारित पैनल पर प्रस्तुत करने के लिए मजबूर करने की संभावना को रोकता है।

एक अन्य संभावना विपरीत दिशा में जाने की है: एक स्विंग एप्लिकेशन बनाएं और जावाएफएक्स नियंत्रणों को एम्बेड करें, जैसे कि जेएफएक्सपैनल का उपयोग करना ; हालाँकि, यह धब्बा व्यवहार को स्वीकार करने के लिए अधिक विवेकपूर्ण प्रतीत होता है जब तक कि बग को धराशायी नहीं किया जाता है।