Word document with java

A Word file is supposed to be created without Word with the help of Java application protocol interfaces.

Concepts Involved: Apache POI and Maven

Apache POI Is an API provided by Apache foundation which is a collection of different java libraries. This facility gives the library to read, write, and manipulate different Microsoft files such as Excel sheets, PowerPoint, and Word files. There are two types basically older version including ‘.doc’, ‘.ppt’ while newer versions of files as ‘.docx’, ‘.pptx’. There are two ways to deal with Apache POI as mentioned below:

Binary Distribution  Source Distribution 
No compilation is required to generate file.jar files  Files to generated needs to be compiled in order to generate file.jar files 
If working with Linux go for the ‘.tar.gz’ version (version 3.5 backward) otherwise simply download the .zip. the file extension for installation to work on Apache POI.  Here format does not matter because file is not compiled so can be altered as per need 

Here zip file is taken for consideration and also if the operating system is Windows, zip files should be preferred. It is a simple java project so binary distribution API is used. In the process of creating the document, several paragraphs will be inserted as a sample to display output and will be providing styles to paragraphs such as font color, font name, and font size. 

Now in order to create a Word file without using Microsoft Word, there is a java interface called Spire and if there is a need to create a PDF document without using Adobe Acrobat then it can be done with the use of an interface known as ‘E-Ice blue’. Here ‘Spire.doc’ must have to be imported as per problem statement as all dealing is in word format.

Spire.Doc for Java is a professional Java Word API that enables Java applications to create, convert, manipulate, and print Word documents without using Microsoft Office. It will be imported as a reference for this program as a reference. 

Syntax: For importing libraries in java of Spire

import Spire.Doc.jar ;

Components Of Spire involved are as follows:

Component Responsibility
XML Word Processor Format (XWPF) Reading and writing in ‘docx’ extensions files of MS-Word
Horrible Word Processor Format (HWPF) Reading and writing in ‘doc’ extensions files of MS-Word 

Additionally, there is another Java API for PDF format as discussed above ‘E-Ice Blue’ that enables developers to read, write, convert, and print PDF documents in Java applications without using Adobe Acrobat. Similarly, a PowerPoint API allows developers to create, read, edit, convert, and print PowerPoint files within Java applications.

If user is creating shear Maven project, one can easily add the jar dependency by adding the following configurations to the pom.xml.

Now, just like any class Apache POI contains classes and methods to work on. Major components of Apache POI are discussed below to understand the internal working of a file, how it is been generated without Word with help of classes and methods. There are basically two versions of files been present in Word itself.

Older files extensions Newer file extension (Version 3.5)
doc docx 
xls xlsx
ppt pptx

Maven 

These files are accessed using Maven. Maven is a powerful project management tool that is based on POM (project object model). It is used for projects to build, dependency, and documentation. It simplifies the build process like ANT. But it is too much advanced than ANT.
In short terms we can tell maven is a tool that can be used for building and managing any Java-based project. Maven makes the day-to-day work of Java developers easier and generally help with the comprehension of any Java-based project.

Maven does have some specific commands to deal with files. Most frequently used are:

poi + poi + scratchpadpoi    // In order to deal with older Word file versions 
poi + poi-ooxml              // In order to deal with new Word file versions

Implementation: Below program depicts the responsibility of Spire API in creating a Word file:

Java

import com.spire.doc.Document;

import com.spire.doc.FileFormat;

import com.spire.doc.Section;

import com.spire.doc.documents.BuiltinStyle;

import com.spire.doc.documents.Paragraph;

import com.spire.doc.documents.ParagraphStyle;

class GFG {

    public static void main(String[] args)

    {

        Document document = new Document();

        Section section = document.addSection();

        Paragraph heading = section.addParagraph();

        heading.appendText("Java");

        Paragraph subheading_1 = section.addParagraph();

        subheading_1.appendText("What's Java");

        Paragraph para_1 = section.addParagraph();

        para_1.appendText(

            "Java is a general purpose, high-level programming language developed by Sun Microsystems."

            + " The Java programming language was developed by a small team of engineers, "

            + "known as the Green Team, who initiated the language in 1991.");

        Paragraph para_2 = section.addParagraph();

        para_2.appendText(

            "Originally called OAK, the Java language was designed for handheld devices and set-top boxes. "

            + "Oak was unsuccessful and in 1995 Sun changed the name to Java and modified the language to take "

            + "advantage of the burgeoning World Wide Web. ");

        Paragraph subheading_2 = section.addParagraph();

        subheading_2.appendText("Java Today");

        Paragraph para_3 = section.addParagraph();

        para_3.appendText(

            "Today the Java platform is a commonly used foundation for developing and delivering content "

            + "on the web. According to Oracle, there are more than 9 million Java developers worldwide and more "

            + "than 3 billion mobile phones run Java.");

        heading.applyStyle(BuiltinStyle.Title);

        subheading_1.applyStyle(BuiltinStyle.Heading_3);

        subheading_2.applyStyle(BuiltinStyle.Heading_3);

        ParagraphStyle style = new ParagraphStyle(document);

        style.setName("paraStyle");

        style.getCharacterFormat().setFontName("Arial");

        style.getCharacterFormat().setFontSize(11f);

        document.getStyles().add(style);

        para_1.applyStyle("paraStyle");

        para_2.applyStyle("paraStyle");

        para_3.applyStyle("paraStyle");

        for (int i = 0;

             i < section.getParagraphs().getCount(); i++) {

            section.getParagraphs()

                .get(i)

                .getFormat()

                .setAfterAutoSpacing(true);

        }

        document.saveToFile(

            "output/CreateAWordDocument.docx",

            FileFormat.Docx);

    }

}

Output:

Доброе время суток.
Цель: приложение на javafx в которое заносится определенная информация (в текст боксы, чек. радио) и в дальнейшем на выход получаем word файл. Усложняется проблема тем, что эта информация добавляется в существующий документ в определенное место.
Имею: очень не большой опыт в javafx, но это поправимо. Также немного опыта в vba на котором уже написана форма для документа. При запуске шаблона word`a открывается форма, в ней мы пишем/выбираем инфу и это все вставляется в нужное место в документе с помощью bookmarka, не особо сложно.
Вопросы: как (через что) передать информацию из формы прямиком в шаблон worda? Нужно ли затрагивать в этом случае vba (думаю, да)? На сколько разумна такая схема?
Если дадите ответы или пнете в нужном направлении — буду премного благодарен.
Спасибо за внимание и потраченное время.


  • Вопрос задан

    более трёх лет назад

  • 335 просмотров

В моей недавней работе мне нужно было сгенерировать документ Word в программе на Java. После поиска в Интернете и тестирования я успешно решил эту проблему. Сегодня я расскажу вам, как я использую Free Spire.Doc for Java для создания документов Word в приложениях Java, вставки в них изображений и форматирования абзацев.

Конфигурация среды

Установите пакет jar через репозиторий Maven, и код для настройки файла pom.xml выглядит следующим образом:

<repositories>
     <repository>
          <id>com.e-iceblue</id>
          <name>e-iceblue</name>
          <url>http://repo.e-iceblue.com/nexus/content/groups/public/</url>
     </repository>
</repositories>
<dependencies>
     <dependency>
          <groupId>e-iceblue</groupId>
          <artifactId>spire.doc.free</artifactId>
          <version>2.7.3</version>
     </dependency>
</dependencies>

Код Java

import com.spire.doc.*;
import com.spire.doc.documents.HorizontalAlignment;
import com.spire.doc.documents.Paragraph;
import com.spire.doc.documents.ParagraphStyle;
import com.spire.doc.fields.DocPicture;

import java.awt.*;

public class CreateWordDocument {
    public static void main(String[] args){
        //Создать документ Word
        Document document = new Document();

        //Добавить раздел
        Section section = document.addSection();

        //Добавить 4 абзаца в раздел
        Paragraph para1 = section.addParagraph();
        para1.appendText("Земля́");

        Paragraph para2 = section.addParagraph();
        para2.appendText("третья по удалённости от Солнца планета Солнечной системы. "+
                "Самая плотная, пятая по диаметру и массе среди всех планет и крупнейшая среди планет земной группы, в которую входят также Меркурий, Венера и Марс. "+
                "Единственное известное человеку в настоящее время тело Солнечной системы в частности и Вселенной вообще, населённое живыми организмами.");

        Paragraph para3 = section.addParagraph();
        para3.appendText("В публицистике и научно-популярной литературе могут использоваться синонимические термины — "+
                "мир, голубая планета");

        //Добавьте картинки в абзац 4
        Paragraph para4 = section.addParagraph();
        DocPicture picture = para4.appendPicture("pic2.jpg");
        //Установить ширину изображения
        picture.setWidth(300f);
        //Установить высоту изображения
        picture.setHeight(250f);

        //Используйте первый абзац в качестве заголовка и отформатируйте заголовок
        ParagraphStyle style1 = new ParagraphStyle(document);
        style1.setName("titleStyle");
        style1.getCharacterFormat().setBold(true);
        style1.getCharacterFormat().setTextColor(Color.BLUE);
        style1.getCharacterFormat().setFontName("Arial");
        style1.getCharacterFormat().setFontSize(12f);
        document.getStyles().add(style1);
        para1.applyStyle("titleStyle");

        //Установите формат абзацев 2 и 3
        ParagraphStyle style2 = new ParagraphStyle(document);
        style2.setName("paraStyle");
        style2.getCharacterFormat().setFontName("Arial");
        style2.getCharacterFormat().setFontSize(11f);
        document.getStyles().add(style2);
        para2.applyStyle("paraStyle");
        para3.applyStyle("paraStyle");

        //Установите абзац 1 и абзац 4 для выравнивания по центру по горизонтали
        para1.getFormat().setHorizontalAlignment(HorizontalAlignment.Center);
        para4.getFormat().setHorizontalAlignment(HorizontalAlignment.Center);

        //Установите отступ начала второго и третьего абзацев
        para2.getFormat().setFirstLineIndent(25f);
        para3.getFormat().setFirstLineIndent(25f);

        //Установите пробел после абзаца 1, 2 и 3
        para1.getFormat().setAfterSpacing(15f);
        para2.getFormat().setAfterSpacing(10f);
        para3.getFormat().setAfterSpacing(10f);

        //Сохраните документ
        document.saveToFile("Word Document.docx", FileFormat.Docx);
    }
}

image

From parameter files, to raw data, to debuggin, it can be really useful to manually load text files natively in the application you’re trying to develop. That way you can ensure that the information your program is ingesting is the stuff you meant to put in.

It’s also the first step on the way to creating more complex applications like hex editors and development aids. in this article, I’ll go through the step by step process of building that editor.

A Simple JavaFX Text Editor

In JavaFX, text can be loaded and displayed in a TextArea by parsing text using a BufferedReader as a list of strings. This can be trasnferred to a TextArea by concatenating the list and invoking setText(String) on the TextArea.

What you’ll get from this article

The user interface in a text reader needs to have three main components:

  1. A route through which the user can select a file. This can be drag-and-drop, or through a file chooser, but I’ll just use a file chooser for today.
  2. A Text area to display the contents of the file.

Additionally, we can upgrade this functionality in two ways:

  1. Running a background process to determine if the file has been edited outside of the application
  2. Enabling the user to save the file themselves through either keyboard shortcuts, or menu selection.
  3. Keeping track of the current file status so that if a user loads in another text file, they are prompted should changes need to be saved.

By the end of it, we will have a basic text editor capable of loading, synching and saving a text document.

JavaFX Application information flow and method calls for a basic text editor

Oh my days, I just need the code please :)!

That’s OK. There’s a full code example at the end of a working JavaFX Text Editor. You can jump down with this button and read the rest when you have the time.

Jump To Code!

Designing the User Interface

To facilitate an interface that can open, edit, sync and save a text file, we’ll need three basic elements to our view.

A TextA

  1. A simple menu to open, save and close the file
  2. A TextArea to display the contents of the file
  3. A status label and progress bar to display loading progress
  4. A button to let the user load changes if they occur outside of the program
Sime File Editor FXML WireFrame

I’ve tried to keep it as simple as possible – really just the bare bones of what you need. You may want to improve and style it yourself if you use the program, but here’s the simple FXML markup to get you started:

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<BorderPane xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.edencoding.controllers.SimpleFileEditorController">
   <top>
       <MenuBar>
           <Menu text="File">
               <MenuItem text="Open" />
               <MenuItem text="Save" />
               <MenuItem text="Close" />
           </Menu>
       </MenuBar>
   </top>
   <center>
       <AnchorPane>
           <TextArea editable="false" prefHeight="400.0" prefWidth="600.0" text="Load a text file using the menu" AnchorPane.bottomAnchor="5.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0" AnchorPane.topAnchor="5.0" />
       </AnchorPane>
   </center>
   <bottom>
       <HBox>
           <padding>
               <Insets bottom="5.0" left="5.0" right="5.0" />
           </padding>
           <HBox alignment="CENTER_LEFT" HBox.hgrow="ALWAYS">
               <Label fx:id="statusMessage" prefWidth="150.0" text="Checking for Changes..." />
               <ProgressBar fx:id="progressBar" prefWidth="150.0" progress="0.0" />
           </HBox>
           <HBox alignment="CENTER_RIGHT" HBox.hgrow="ALWAYS">
               <Button fx:id="loadChangesButton" mnemonicParsing="false" text="Load Changes" />
           </HBox>
       </HBox>
   </bottom>
</BorderPane>

Note: I’ve exposed the Label, ProgressBar and Button using fx:id tags, because I’ll want to control when they’re visible based on the application’s current state.

Finally, we’ll create the Controller, which will define the actual business logic of loading a file into our View, and maintaining it in the programme’s memory. You can see I’ve already linked the FXML file to a Java file using the attribute fx:controller. You’ll need to link yours up too.

A basic Controller doesn’t actually need any code in it. it should by default look like this:

package com.edencoding.controllers;
public class SimpleFileEditorController {
    public void initialize(){
        loadChangesButton.setVisible(false);
    }
}

What I have added is a little code to hide the “load file changes” button, because we won’t want to see that until we’ve detected changes later on. There are actually a lot of ways to hide a button in JavaFX, with different effects on the interface. Here’s setting the visible property will suit just fine.

Next, we’ll add the Java code to control how users open a text file.

How to display a text file in a TextArea

We’ll use JavaFX events to start the sequence of loading a file into the text area in four stages:

  1. Set an action on the “Open” menu item using the FXML # operator
  2. Use that method to let the user choose a file to load.
  3. Read a text file into memory
  4. Display the file contents
The loading process for a text editor built with JavaFX

To set a method on a button action, we need to use the onAction attribute of the MenuItem object in FXML.

Inside the FXML file above, modify the “Open” MenuItem so that it looks like this:

<MenuItem text="Open" onAction="#chooseFile"/>

After that, we’ll need to define the chooseFile() method in our Controller.

2. Choosing a file to load

The controller should be looking pretty blank at the moment, so we’ll need to add a method to let our user choose a file when prompted. The prototype for this method is as so:

public void chooseFile(ActionEvent event);

Because it originates from a MenuItem wired in from the FXML file, the method by default takes an ActionEvent as a parameter, which will be automatically generated and passed to the method by JavaFX when the MenuItem is clicked.

Don’t worry, we don’t have to do anything with the event except consume it once we’re finished. If you want to find out more about event propagation, and everything about JavaFX events, check out my comprehensive guide. It goes into depth about how to create them and use them effectively.

Right now we need to let our user load the file. Inside the method:

public void chooseFile(ActionEvent event) {
    FileChooser fileChooser = new FileChooser();
    //only allow text files to be selected using chooser
    fileChooser.getExtensionFilters().add(
            new FileChooser.ExtensionFilter("Text files (*.txt)", "*.txt")
    );
    //set initial directory somewhere user will recognise
    fileChooser.setInitialDirectory(new File(System.getProperty("user.home")));
    //let user select file
    File fileToLoad = fileChooser.showOpenDialog(null);
    //if file has been chosen, load it using asynchronous method (define later)
    if(fileToLoad != null){
        loadFileToTextArea(fileToLoad);
    }
}

What we get at the end of the process is a File object that refers to the data on the hard disk – we haven’t loaded anything into the Application memory yet.

Note: because we’re compartmentalising our code nicely, once we’ve successfully selected a file, we can delegate the next action to another method – loadFileToTextArea(File file), which will do the heavy lifting of loading the file itself.

3. Reading a text file in Java

To read and display the text file, we’ll create a Task that’s going to:

  1. Load our file in the background
  2. Display the contents of the file in the TextArea when it’s done.

Then:

  1. We’ll bind the Task to the ProgressBar, so it updates the user on the file load process. In reality, most small file loads should be fast enough that this won’t be visible, but I always think it’s good practice to build in some

I’d also recommend an obvious extension to this would be to check the file size (perhaps in bytes) and load it in chunks, but I won’t complicate the code with that here.

a. Loading our file in the background with UI updates

Loading text into the programme memory is no different in JavaFX than it would be in plain old Java, so we can do this using a BufferedReader to transcribe the text file’s contents into

private Task<String> fileLoaderTask(File fileToLoad){
    //Create a task to load the file asynchronously
    Task<String> loadFileTask = new Task<>() {
        @Override
        protected String call() throws Exception {
            BufferedReader reader = new BufferedReader(new FileReader(fileToLoad));
            //Use Files.lines() to calculate total lines - used for progress
            long lineCount;
            try (Stream<String> stream = Files.lines(fileToLoad.toPath())) {
                lineCount = stream.count();
            }
            //Load in all lines one by one into a StringBuilder separated by "n" - compatible with TextArea
            String line;
            StringBuilder totalFile = new StringBuilder();
            long linesLoaded = 0;
            while((line = reader.readLine()) != null) {
                totalFile.append(line);
                totalFile.append("n");
                updateProgress(++linesLoaded, lineCount);
            }
            return totalFile.toString();
        }
    };
    //If successful, update the text area, display a success message and store the loaded file reference
    loadFileTask.setOnSucceeded(workerStateEvent -> {
        try {
            textArea.setText(loadFileTask.get());
            statusMessage.setText("File loaded: " + fileToLoad.getName());
            loadedFileReference = fileToLoad;
        } catch (InterruptedException | ExecutionException e) {
            Logger.getLogger(getClass().getName()).log(SEVERE, null, e);
            textArea.setText("Could not load file from:n " + fileToLoad.getAbsolutePath());
        }
    });
    //If unsuccessful, set text area with error message and status message to failed
    loadFileTask.setOnFailed(workerStateEvent -> {
        textArea.setText("Could not load file from:n " + fileToLoad.getAbsolutePath());
        statusMessage.setText("Failed to load file");
    });
    return loadFileTask;
}

Here, we can use the setOnFailed() method to display helpful error messages to the user. We can also use the setOnSucceeded() method to handle pushing the new content into the TextArea.

4. Bind the Task to our ProgressBar and load the text to the TextArea

Now we have a Task we can bind its progress to the progress displayed in the ProgressBar, and set it running to load our file in the background.

We’ll do all of this in the loadFileToTextArea() method we referenced above.

private void loadFileToTextArea(File fileToLoad) {
    Task<String> loadFileTask = fileLoaderTask(fileToLoad);
    progressBar.progressProperty().bind(loadFileTask.progressProperty());
    loadFileTask.run();
}

How to update the display if the text file changes

There are a few ways to keep track of whether a text file has changed, but by far the easiest is to load the file attributes and check the date and time the file was last modified. To do this in the background we’ll add code in three stages:

  1. Saving the file details and scheduling checking to detect when these change
  2. Using JavaFX events in the ScheduledService to notify the user
  3. Handling whether the user wants to load the changes

JavaFX Application for text editing, diagram detailing the process for detecting changes in text files on the hard drive

1. Saving file details and scheduling checking

We’ll start by saving the time that the file was last modified (lastModifiedTime) as we load the file by adding a line of code to the setOnSucceeded() method of the Task we defined above:

loadFileTask.setOnSucceeded(workerStateEvent -> {
    try {
        textArea.setText(loadFileTask.get());
        statusMessage.setText("File loaded: " + fileToLoad.getName());
        loadedFileReference = fileToLoad;
        lastModifiedTime = Files.readAttributes(fileToLoad.toPath(), BasicFileAttributes.class).lastModifiedTime();
    } catch (InterruptedException | ExecutionException | IOException e) {
        Logger.getLogger(getClass().getName()).log(SEVERE, null, e);
        textArea.setText("Could not load file from:n " + fileToLoad.getAbsolutePath());
    }
});

Obviously make sure you define lastModifiedTime and loadedFileReference as member variables of the controller :).

private File loadedFileReference;
private FileTime lastModifiedTime;

Now, we can schedule a process to check the time the file was last modified every second. To make sure we keep the UI responsive during the check-and-load cycle, we can run them through a background process that’s scheduled to run periodically.

Scheduling file checking to detect changes

Here, we’ll dig into the JavaFX concurrency package a little to schedule a Task using the ScheduledService object. This is one of a huge number of ways to schedule a Task in JavaFX.

If you’re interested in learning more about your concurrency and scheduling options, and how you can tune your efficiency against your convenience, check out my guide to JavaFX running background processes.

For now, we’ll create a ScheduledService, defining a Task that checks whether the file has been modified since we last loaded it.

private ScheduledService<Boolean> createFileChangesCheckingService(File file){
    ScheduledService<Boolean> scheduledService = new ScheduledService<>() {
        @Override
        protected Task<Boolean> createTask() {
            return new Task<>() {
                @Override
                protected Boolean call() throws Exception {
                    FileTime lastModifiedAsOfNow = Files.readAttributes(file.toPath(), BasicFileAttributes.class).lastModifiedTime();
                    return lastModifiedAsOfNow.compareTo(lastModifiedTime) > 0;
                }
            };
        }
    };
    scheduledService.setPeriod(Duration.seconds(10));
    return scheduledService;
}

We can define the event that will be fired whenever this ScheduledService detects a file change by setting it after it’s created. This event will be fired whenever the Task succeeds.

To get this service running, we’

2. Using JavaFX events to notify the user

Inside the setOnSucceeded() method above, we used a WorkerStateEvent (an event triggered by the status of our task) to fire a method call to notify our user.

Now, we want to define what happens once we’ve detected a change. For a start, we’ll want to stop checking whether the file’s changed, because it already has. Then, we can notify the user and let them decide whether to load the changes.

private void scheduleFileChecking(File file){
    ScheduledService<Boolean> fileChangeCheckingService = createFileChangesCheckingService(file);
    fileChangeCheckingService.setOnSucceeded(workerStateEvent -> {
        //first time task runs, this may be null. Avoid NPE.
        if(fileChangeCheckingService.getLastValue()==null) return;
        if(fileChangeCheckingService.getLastValue()){
            //no need to keep checking
            fileChangeCheckingService.cancel();
            notifyUserOfChanges();
        }
    });
    System.out.println("Starting Checking Service...");
    fileChangeCheckingService.start();
}

Of course, we need to set that going, and a sensible point to start checking is when we initially load the file:

loadFileTask.setOnSucceeded(workerStateEvent -> {
    //beneath the try-catch block:
    scheduleFileChecking(loadedFileReference);
});

With the ScheduledService started, we can now notify the user and await their response!

3. Handling whether the user wants to load the changes

To notify the user of changes to the file, we’ll very simply show the button to he user:

private void notifyUserOfChanges() {
    loadChangesButton.setVisible(true);
}

Now, we need to define the action that will be completed if the user decides to load the changes, which we’ll do in two steps:

  1. Add an action to the button in FXML using the # operator
  2. Create a connected method in the Controller to load the changes based on the file reference we’ve already saved for this file.
FXML:
<Button fx:id="loadChangesButton" onAction="#loadChanges" text="Load Changes" />
Controller:
public void loadChanges(ActionEvent event){
    loadFileToTextArea(loadedFileReference);
    loadChangesButton.setVisible(false);
}

Now, once the user selects the option to re-load the changes, the loadFileToTextArea() will take care of loading the file, and restarting the ScheduledService, which will check for future changes.

How to save TextArea content to a file

Compared to scheduling checks and maintaining a file, saving the contents of our file back into its original location is comparatively simple. Again, the first step is to register an action on the button in the FXML file, and then to create the method in our Controller.

FXML:
<MenuItem text="Save" onAction="#saveFile"/>
Controller:
public void saveFile(ActionEvent event) {
    try {
        FileWriter myWriter = new FileWriter(loadedFileReference);
        myWriter.write(textArea.getText());
        myWriter.close();
        lastModifiedTime = FileTime.fromMillis(System.currentTimeMillis() + 3000);
        System.out.println("Successfully wrote to the file.");
    } catch (IOException e) {
        Logger.getLogger(getClass().getName()).log(SEVERE, null, e);
    }
}

And that’s it! Note I’m also resetting the lastModifiedTime attribute a few seconds into the future so we don’t accidentally pick up the file change in our scheduler. There’s undoubtedly a more elegant event-based way to do this, but for the short example here, it’s absolutely fine.

Full Code Example

In case you want the full, complete code to play with, here it is. You’ll need to adjust the file references based on how you want to store resources in your project, but here’s how I organised mine.

Project Directory
│   pom.xml //I used Maven
│   
└───src
    └───main
        ├───java
        │   │   module-info.java  //I used a modular project
        │   │   
        │   └───com
        │       └───edencoding
        │           │   SimpleFileEditor.java
        │           │   
        │           └───controllers
        │                   SimpleFileEditorController.java
        │                   
        └───resources
            │   
            └───com
                └───edencoding
                    ├───fxml
                    │       SimpleFileEditor.fxml
                    │           
                    └───img
                            EdenCodingIcon.png
                            

SimpleFileEditor.java (App Launcher)

This is the code for the entry point to the application. Modify code here to change the windowing options like icon, window title and window size.

package com.edencoding;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.stage.Stage;
public class SimpleFileEditor extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("fxml/SimpleFileEditor.fxml"));
        Parent root = loader.load();
        primaryStage.getIcons().add(new Image(getClass().getResourceAsStream("img/EdenCodingIcon.png")));
        primaryStage.setTitle("Simple EdenCoding JavaFX File Editor");
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }
}

SimpleFileEditor.fxml (View)

This is the code for the FXML file in the application. Change code here to modify how the view looks.

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<BorderPane xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1"
            fx:controller="com.edencoding.controllers.SimpleFileEditorController">
    <top>
        <MenuBar>
            <Menu text="File">
                <MenuItem text="Open" onAction="#openFile"/>
                <MenuItem text="Save" onAction="#saveFile"/>
                <MenuItem text="Close"/>
            </Menu>
        </MenuBar>
    </top>
    <center>
        <AnchorPane>
            <TextArea fx:id="textArea" prefHeight="400.0" prefWidth="600.0" text="Load a text file using the menu"
                      AnchorPane.bottomAnchor="5.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0"
                      AnchorPane.topAnchor="5.0"/>
        </AnchorPane>
    </center>
    <bottom>
        <HBox>
            <padding>
                <Insets bottom="5.0" left="5.0" right="5.0"/>
            </padding>
            <HBox alignment="CENTER_LEFT" HBox.hgrow="ALWAYS">
                <Label fx:id="statusMessage" prefWidth="150.0" text="Checking for Changes..."/>
                <ProgressBar fx:id="progressBar" prefWidth="150.0" progress="0.0"/>
            </HBox>
            <HBox alignment="CENTER_RIGHT" HBox.hgrow="ALWAYS">
                <Button fx:id="loadChangesButton" onAction="#loadChanges" text="Load Changes"/>
            </HBox>
        </HBox>
    </bottom>
</BorderPane>

SimpleFileEditorController.java

This is the code for Controller of the view. Modify code here to change how the application behaves.

package com.edencoding.controllers;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.TextArea;
import javafx.stage.FileChooser;
import javafx.util.Duration;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.util.concurrent.ExecutionException;
import java.util.logging.Logger;
import java.util.stream.Stream;
import static java.util.logging.Level.SEVERE;
public class SimpleFileEditorController {
    private File loadedFileReference;
    private FileTime lastModifiedTime;
    public Label statusMessage;
    public ProgressBar progressBar;
    public Button loadChangesButton;
    public TextArea textArea;
    public void initialize() {
        loadChangesButton.setVisible(false);
    }
    public void openFile(ActionEvent event) {
        FileChooser fileChooser = new FileChooser();
        //only allow text files to be selected using chooser
        fileChooser.getExtensionFilters().add(
                new FileChooser.ExtensionFilter("Text files (*.txt)", "*.txt")
        );
        //set initial directory somewhere user will recognise
        fileChooser.setInitialDirectory(new File(System.getProperty("user.home")));
        //let user select file
        File fileToLoad = fileChooser.showOpenDialog(null);
        //if file has been chosen, load it using asynchronous method (define later)
        if (fileToLoad != null) {
            loadFileToTextArea(fileToLoad);
        }
    }
    private void loadFileToTextArea(File fileToLoad) {
        Task<String> loadTask = fileLoaderTask(fileToLoad);
        progressBar.progressProperty().bind(loadTask.progressProperty());
        loadTask.run();
    }
    private Task<String> fileLoaderTask(File fileToLoad) {
        //Create a task to load the file asynchronously
        Task<String> loadFileTask = new Task<>() {
            @Override
            protected String call() throws Exception {
                BufferedReader reader = new BufferedReader(new FileReader(fileToLoad));
                //Use Files.lines() to calculate total lines - used for progress
                long lineCount;
                try (Stream<String> stream = Files.lines(fileToLoad.toPath())) {
                    lineCount = stream.count();
                }
                //Load in all lines one by one into a StringBuilder separated by "n" - compatible with TextArea
                String line;
                StringBuilder totalFile = new StringBuilder();
                long linesLoaded = 0;
                while ((line = reader.readLine()) != null) {
                    totalFile.append(line);
                    totalFile.append("n");
                    updateProgress(++linesLoaded, lineCount);
                }
                return totalFile.toString();
            }
        };
        //If successful, update the text area, display a success message and store the loaded file reference
        loadFileTask.setOnSucceeded(workerStateEvent -> {
            try {
                textArea.setText(loadFileTask.get());
                statusMessage.setText("File loaded: " + fileToLoad.getName());
                loadedFileReference = fileToLoad;
                lastModifiedTime = Files.readAttributes(fileToLoad.toPath(), BasicFileAttributes.class).lastModifiedTime();
            } catch (InterruptedException | ExecutionException | IOException e) {
                Logger.getLogger(getClass().getName()).log(SEVERE, null, e);
                textArea.setText("Could not load file from:n " + fileToLoad.getAbsolutePath());
            }
            scheduleFileChecking(loadedFileReference);
        });
        //If unsuccessful, set text area with error message and status message to failed
        loadFileTask.setOnFailed(workerStateEvent -> {
            textArea.setText("Could not load file from:n " + fileToLoad.getAbsolutePath());
            statusMessage.setText("Failed to load file");
        });
        return loadFileTask;
    }
    private void scheduleFileChecking(File file) {
        ScheduledService<Boolean> fileChangeCheckingService = createFileChangesCheckingService(file);
        fileChangeCheckingService.setOnSucceeded(workerStateEvent -> {
            if (fileChangeCheckingService.getLastValue() == null) return;
            if (fileChangeCheckingService.getLastValue()) {
                //no need to keep checking
                fileChangeCheckingService.cancel();
                notifyUserOfChanges();
            }
        });
        System.out.println("Starting Checking Service...");
        fileChangeCheckingService.start();
    }
    private ScheduledService<Boolean> createFileChangesCheckingService(File file) {
        ScheduledService<Boolean> scheduledService = new ScheduledService<>() {
            @Override
            protected Task<Boolean> createTask() {
                return new Task<>() {
                    @Override
                    protected Boolean call() throws Exception {
                        FileTime lastModifiedAsOfNow = Files.readAttributes(file.toPath(), BasicFileAttributes.class).lastModifiedTime();
                        return lastModifiedAsOfNow.compareTo(lastModifiedTime) > 0;
                    }
                };
            }
        };
        scheduledService.setPeriod(Duration.seconds(1));
        return scheduledService;
    }
    private void notifyUserOfChanges() {
        loadChangesButton.setVisible(true);
    }
    public void loadChanges(ActionEvent event) {
        loadFileToTextArea(loadedFileReference);
        loadChangesButton.setVisible(false);
    }
    public void saveFile(ActionEvent event) {
        try {
            FileWriter myWriter = new FileWriter(loadedFileReference);
            myWriter.write(textArea.getText());
            myWriter.close();
            lastModifiedTime = FileTime.fromMillis(System.currentTimeMillis() + 3000);
            System.out.println("Successfully wrote to the file.");
        } catch (IOException e) {
            Logger.getLogger(getClass().getName()).log(SEVERE, null, e);
        }
    }
}

Conclusions

It’s actually surprisingly simple to create a text editor in JavaFX.

In this case, we combined the well-defined methods of loading text files in Java with the powerful event-driven mechanics of JavaFX.

At the end, we have a simple, easy to use text editor that can be used to open, sync and save text files on the hard drive.

Стоит задача формирования отчета в виде документа MS Word (.docx).
Сам документ должен содержать заголовок и таблицу из нескольких столбцов.

Для реализации наткнулся на Apache POI.
В прицнипе, все просто:

XWPFDocument doc = new XWPFDocument();
XWPFTable table = doc.createTable(3, 3);    
table.getRow(1).getCell(1).setText("EXAMPLE OF TABLE");
table.getRow(2).getCell(2).setText("only text");

FileOutputStream out = new FileOutputStream("simpleTable.docx");
doc.write(out);
out.close();

Но! Уж очень он тяжеловесный, для такой простой задачи придется тащить

xmlbeans*.jar
poi*.jar
poi-ooxml*.jar
poi-ooxml-schemas*.jar

А это где-то 10Мб!

Вопрос — есть ли более легковесное решение?
На выходе нужен .docx файл, содержащий табличку.

aleksandr barakin's user avatar

задан 7 сен 2015 в 13:40

kry4a's user avatar

Есть еще один вариант, без сторонних библиотек.

Документ docx (Office Open XML) представляет собой набор XML-файлов в zip-архиве. Имея это в виду, вполне можно средствами XSLT-преобразований сгенерировать из шаблона готовый отчет.

PS. Если у вас нет жестких внешних ограничений, 10 МБ — не тот размер из-за которого стоит паниковать.

ответ дан 7 сен 2015 в 14:48

Nofate's user avatar

NofateNofate

34.3k15 золотых знаков63 серебряных знака92 бронзовых знака

Есть, минимум, два пути.
Работать с Word через COM. Но, тогда программа будет работать только под Windows.
Как вариант, можно ещё посмотреть в сторону WordPrecessing. Но получится сделать то, что Вы хотите или нет, не уверен.

ответ дан 7 сен 2015 в 14:42

Streletz's user avatar

StreletzStreletz

11.7k9 золотых знаков27 серебряных знаков39 бронзовых знаков

Могу порекомендовать компонент компании Aspose: Aspose.Words. Конкретно под Java его не использовал, использовал под .NET, но от него остались исключительно позитивные впечатления. Продукт платный.

ответ дан 7 сен 2015 в 14:49

Daniel Vygolov's user avatar

1

Если файл с полностью фиксированной разметкой, решением для бедных будет набить шаблон прямо в ворде типа такого:

Word

Сохраняете его в .docx и добавляете в ресурсы своего приложения. Файл с картинки весит 10Kb. Затем в процессе выполнения программы распаковываете его как zip-архив (например, с помощью стандартного ZipFile), там есть файл word/document.xml, в котором по факту всё содержимое. Читаете его в память (в кодировке UTF-8) и делаете поиск с заменой, заменяя всякие {{{Header1}}} на произвольное содержимое (не забудьте, что надо эскейпить в XML <, > и &). Генерируете новый zip-файл с расширением .docx (можно использовать стандартный ZipOutputStream). Туда записываете изменённый word/document.xml, остальные файлы копируете из ресурсного зипа без изменений.

Если требуются структурные изменения, можно генерировать document.xml с помощью DOM или XSLT. В принципе в таком варианте тоже ничего сложного.

ответ дан 7 сен 2015 в 15:52

Tagir Valeev's user avatar

Tagir ValeevTagir Valeev

5,49915 серебряных знаков41 бронзовый знак

Понравилась статья? Поделить с друзьями:
  • Word document with all fonts
  • Word document will not upload
  • Word document will not save changes
  • Word document will not edit
  • Word document will not convert to pdf