I am writing an app that generates Maths worksheets for school students. It will, for example, generate 2 to 5 pages of simple Maths questions and 1 to 2 pages of answers. The PDF can be saved to file and loaded again later. Then it has a print function that can print all the pages. I want to make it skip printing the answer pages.
Is it possible to automatically identify which pages are the answer pages? I can only think of a workaround by making those answer pages have special height or width but not even sure if this works. Are there any better ways to do this?
Ok, I continued the project and used the following method: when constructing the PDF, I put the word "Answer on the top left corner with a gray rectangle surrounding it drawn with drawRect(). Then before the actual printing, I used the following code inside the PrintDocumentAdapter() class to check whether the color of the pixel 0,0 is gray or not.
#Override
public void onStart() {
if (parcelFileDescriptor != null) {
try {
pdfRenderer = new PdfRenderer(parcelFileDescriptor);
} catch (IOException e) {
e.printStackTrace();
}
}
int tempTotal = pdfRenderer.getPageCount();
Bitmap[] tempBitmap = new Bitmap[tempTotal];
finalTotal = tempTotal;
for (int pageNum = 0; pageNum < tempTotal; pageNum++) {
PdfRenderer.Page tempPage = pdfRenderer.openPage(pageNum);
tempBitmap[pageNum] = Bitmap.createBitmap(WS_WIDTH, WS_HEIGHT, Bitmap.Config.ARGB_8888);
tempPage.render(tempBitmap[pageNum], null, null, PdfRenderer.Page.RENDER_MODE_FOR_PRINT);
if (tempBitmap[pageNum].getPixel(0, 0) == Color.GRAY) {
finalTotal--;
}
tempPage.close();
}
}
It works fine. At least should cause no problem if the users only attempt to print PDF files constructed with my app. :P
Please tell me if you know a better way to do this. Thanks!
After I solved my problem on docx4j previously, I'm be able to use it now.
I just try to run the sample code from this link
http://www.smartjava.org/content/create-complex-word-docx-documents-programatically-docx4j
with some modification.
let's say I have two documents.
One is main template that have about 2-3 pages.
Second one have only 1 paragraph of text with various of style (Bold, Italic, Underline, Font Size, etc).
I want to replace a parameter in my template with a paragraph in the second document.
The result is it can replace my parameter with a paragraph but there is a problem with style. What I can observe with many experiment is:
Indent still there
New Line still there
Underline move along too
Font Color/ Font Size is working
Bold/Italic not come along
Font Family not come along
Here is my code
private static void replaceParagraph2(String placeholder, WordprocessingMLPackage template, ContentAccessor addTo) throws Exception {
//get the paragraph
WordprocessingMLPackage paragraph_template = getTemplate("./resources/input/paragraph.docx");
List<Object> paragraphs_LineList = getAllElementFromObject(paragraph_template.getMainDocumentPart(), P.class);
// get the template
List<Object> template_lineList = getAllElementFromObject(template.getMainDocumentPart(), P.class);
int position = 0;
P toReplace = null;
//find placeholder position
for (Object p : template_lineList) {
List<Object> texts = getAllElementFromObject(p, Text.class);
for (Object t : texts) {
Text content = (Text) t;
if (content.getValue().equals(placeholder)) {
toReplace = (P) p;
position = template_lineList.indexOf(toReplace);
break;
}
}
}
//add paragraph into template
for (int i = 0; i < paragraphs_LineList.size(); i++) {
P para = (P) XmlUtils.deepCopy(paragraphs_LineList.get(i));
addTo.getContent().add(position + 1 + i, para);
}
// remove the placeholder on the template
((ContentAccessor)toReplace.getParent()).getContent().remove(toReplace);
}
Do I missing something?
PS. I debug to check the object of template. It seems that bold value in the P object is config to null. (It's booleanTrueifNull type I think.)
The formatting comes from direct formatting (in rPr and pPr elements in the paragraph), and also from the styles part. If no style is specified, the default styles will be used.
You'll need to look at the XML in your paragraph, and in the styles part.
Microsoft Word (2010 at least) has a useful "Reveal Formatting" sidebar which can help you to understand where the formatting is coming from. Click the "distinguish style source" at the bottom.
There is code in docx4j (used by its PDF output) to determine the effective formatting. I guess you could use that to specifically apply the effective formatting from your source to each run in your target.
I am trying to implement a text editor in NetBeans with simple functions like: text styling (bold, italic, underlined ...), open file, save file and search. Search function searches for a specified string in the document and highlights the results. The problem occurs when I am trying to remove those highlights or add new ones for different search. Currently I use StyledDocument object together with jTextPane.
private void textHighlight(int startAt, int endAt, Color c) {
Style sCh;
sCh = textEditor.addStyle("TextBackground", null);
StyleConstants.setBackground(sCh, c);
StyledDocument sDoc = textEditor.getStyledDocument();
sDoc.setCharacterAttributes(startAt, endAt - startAt, sCh, false);
}
private void textFind(java.awt.event.ActionEvent evt) {
int searchIndex = 0;
String searchValue = searchField.getText();
if(lastIndex != -1) {
while(searchIndex < lastIndex) {
countOccurencies++;
int i = textEditor.getText().indexOf(searchValue, searchIndex);
textHighlight(i, i+searchValue.length(), Color.MAGENTA);
searchIndex = i+searchValue.length();
}
statusLabel.setText(countOccurencies + " rezultatov.");
} else {
statusLabel.setText("Ni rezultatov!");
}
}
}
private void searchEnd(java.awt.event.ActionEvent evt) {
textEditor.removeStyle("TextBackground");
}
removeStyle() doesn't seem to be working.
Must admit I don't know how Styles work, but maybe the attributes of the Style are copied to the Document at the time you add the Style?
Another option is to use the Highlighter class. See textPane.getHighlighter(). Then you can keep track of the individual highlights you add in an ArrayList and then use the ArrayList to remove the highlights when you want the clear the text pan.
Also, inside your search loop you have a couple of problems:
Don't use the getText() method. This can cause problems with text offsets being off by one for every line of text in the text pane. See Text and New Lines for more information and the solution.
You are getting the text inside the loop which is not very efficient. You should only get the text once outside the loop.
I am writing a software (GWT) in which different images of soccerclubs and its names should be shown. So i wrote the following method:
public void updateClubNameHeader(String clubName, int logoNumber) {
clubLabelFrame.clear();
HTML clubLogo = new HTML();
clubLogo.setStyleName("clublogo_img");
clubLabelFrame.add(clubLogo);
Label clubNameLabel = new Label(clubName);
clubNameLabel.setStyleName("clubnameLabel"); <--- logoNumber?
clubLabelFrame.add(clubNameLabel);
}
It is no problem to change the names of the club (clubNameLabel), but if i want to have a new logo as well, this CSS-style is used: (filenames: 1.jpg, 2.jpg, ...)
.clublogo_img {
...
background-image: url("images/1.jpg") !important
}
The problem: If i want to represent every possible club, i have to write round about ~100 styles.
The question: Is it possible to pass my integer logoNumber to the CSS, so that this one style is enough the show all the chosen logos?
You cannot do it with CSS. You can do it with GWT:
clubLogo.getElement().getStyle().setBackgroundImage("url(\"images/" + imageNumber + ".jpg\")");
I have:
JTextPane jtextPane = new JTextPane();
jtextPane.setEditorKit(new HTMLEditorKit());
...
Then later I try to add an unordered list button to the toolbar such that the action is:
Action insertBulletAction =
HTMLEditorKit.InsertHTMLTextAction ("Bullets", "<ul><li> </li></ul>",
HTML.Tag.P, HTML.Tag.UL);
JButton insertBulletJButton = new JButton(insertBulletAction);
And this does include the proper code if I take a dump of the html that's generated. However it will be rendered really badly, as in not even close to reasonable as illustrated below:
However if I do:
jtextPane.setText(jtextPane.getText());
jtextPane.repaint();
Then all is well. But if I don't do BOTH lines, then neither will work alone. I can also make it work by setting the text before I make the jtextPane visible.
This is really odd and I don't understand why I'd have to do a setText(getText()) followed by a repaint().
PS: This is very similar to this question: How to implement bullet points in a JTextPane? And it works except that it's not rendering correctly. I don't know if it has to do with HTMLEditorKit vs RTFEditorKit, but something i causing the rendering to fail. The html source code under is perfect...
PS2: This link is also very handy but it also didn't show a solution.
Update: Here is the full code as requested, but there isn't much else...
public static void main(String[] args)
{
JFrame jframe = new JFrame();
jframe.setSize(800, 600);
jframe.setVisible(true);
JTextPane jtextPane = new JTextPane();
jtextPane.setEditorKit(new HTMLEditorKit());
Action insertBulletAction = new HTMLEditorKit.InsertHTMLTextAction ("Bullets",
"<ul><li> </li></ul>", HTML.Tag.P, HTML.Tag.UL);
JButton insertBulletJButton = new JButton(insertBulletAction);
insertBulletJButton.setRequestFocusEnabled(false);
jframe.setLayout(new BorderLayout());
jframe.add(new JScrollPane(jtextPane));
jframe.add(insertBulletJButton, BorderLayout.SOUTH);
}
The answer is actually quite complex. Basically the InsertHtmlAction is just not good enough by itself. You need a lot of work and logic to get to a working list action. It requires a lot of logic! So you definitely have to override the Action class. Basically the parameters of the InsertHtmlAction will change depending on where in the html code you are.
That being said, I studied several open source solutions to get a better understanding for what was all involved. Many long hours later (and many hours spent beforehand as well), and I was finally able to figure it out well enough for what I needed. But it is fairly complex. Too complex to write about here, it would take a chapter of a book just to explain the concepts. And even then I'm still fuzzy on some details (I'm still working through it).
I can now understand why people sell components for this!
I found that most open source solutions don't really deal nicely with lists. They generally somewhat work but most have glaring bugs. That or they just don't really handle anything but the most basic cases of lists.
Here is a list of systems I looked at to understand how they work to get a better understanding of everything. I unfortunately found the documentation lacking or hard to understand, so looking at these projects helped me more than anything else.
The most helpful
Shef - Most helpful of all.
ekit - Decent but many bugs and not the best code organization
MetaphaseEditor - Similar to ekit
Moderately helpful (more complex, buggy, less relevant, etc.)
OOoBean - Tried but too much (and hence too much complexity) for what I needed. Looks really good though, you just need to invest time.
JXHTMLEdit - Seemed interest
Additional links
JWebEngine - Mostly for rendering
Joeffice - Interesting but it's all videos and wasn't enough ready enough yet.
Richtext - No comments. I only briefly looked at it.
JRichTextEditor - No comments either, same thing.
Paid
JWord - Look very interesting but it was beyond the budget for what I was doing.
For those that need a more specific explanation of HTMLEditorKit's peculiar way of handling lists, it all comes down to the markup generated. I'll try it to keep it as simple as I can. Let's rewind a bit and talk about HTML documents in Swing.
Turns out that Swing relies on paragraphs for doing cursor positioning and navigation. For example, every time you write in a new line, a new pagraph is generated. Even the corresponding view of the document depends on the presence of paragraphs in the right places. There must always be a paragraph in the document. Otherwise, odd things start to happen.
So, what happens if the document is completely blank? Surely, there is no need for a paragraph there. Well, unbelievably, even in that case there is a paragraph. This is one of the effects of what the documentation calls a p-implied or implied paragraph. The HTML generated for a blank document is:
<html>
<head></head>
<body>
<p style="margin-top: 0">
</p>
</body>
</html>
Expectedly, when you insert a list, it is placed inside the paragraph:
<html>
<head></head>
<body>
<p style="margin-top: 0">
<ul>
<li>
</li>
</ul>
</p>
</body>
</html>
... which is of course invalid markup (not just because there is no title inside the head).
But wait! It gets more interesting. Once the list is inserted, the "internal pointer" of the document, as it were, stays after the closing </ul> tag. Consequently, if you type "Hello", it will be placed outside the list:
<html>
<head></head>
<body>
<p style="margin-top: 0">
<ul>
<li>
</li>
</ul>
Hello
</p>
</body>
</html>
This is why that “Hello” appears way to the right relative to the inserted bullet. Now, as Stephane mentioned in the question, setText(getText()) magically solves the problem. That's because manually setting the contents of the JTextPane instance triggers the parser, which in turn places the “internal pointer” where it should be; inside the list. Now when you type “Hello”, it will appear much closer to the bullet. I say much closer because there is still something not right about the HTML:
<html>
<head></head>
<body>
<p style="margin-top: 0">
<ul>
<li>
Hello
</li>
</ul>
</p>
</body>
</html>
Notice there is no paragraph enclosing the new text in the list. That's why the text won't appear right next to the bullet.
How do you go about all this? Well, that's the tricky bit Stephane was talking about. You would be up against a combination of bugs (such as this one), undocumented glitches (like this one) and default behaviour as we have seen. The easiest way out is to use one of the solutions in Stephane's list. I agree Shef is the best of all but has not have that much activity since 2009 (!). Personally, I found Stanislav's website incredibly useful for all things EditorKit.
You can also have a look at ADAPRO: a pretty stable open-source assistive editor I was heavily involved in. The assistive features are buggy but the core editing functionality was thoroughly tested. The following code comes from that project. It requires the ElementWriter class from SHEF's net.atlanticbb.tantlinger.ui.text package.
//HTML representation of an empty paragraph
private static final String sEmptyParagraph = "<p style=\"margin-top: 0\"></p>";
/**
* Translates into HTML a given element of the document model.
* #param element Element to serialise to a HTML string
* #param out Serialiser to HTML string
* #return HTML string "equivalent" to given element
*/
static String extractHTML (Element element, StringWriter out) {
ElementWriter writer = new ElementWriter (out, element);
try {
writer.write();
} catch (IOException e) {
System.out.println ("Error encountered when serialising element: " +e);
e.printStackTrace();
} catch (BadLocationException e) {
System.out.println ("Error encountered when extracting HTML at the element's position: " +e);
e.printStackTrace();
}
return out.toString();
}
/**
* Determines if the parent element of the current paragraph element is one of a number provided as a list
* of tag names. If so, it returns the parent element.
* #param document Document model of the text
* #param iCaretPos Caret's current position
* #param sTags Possible parent tags
* #return Parent element
*/
static Element getNearestParent (HTMLDocument document, int iCaretPos, String sTags) {
Element root;
root = document.getParagraphElement (iCaretPos);
do {
root = root.getParentElement();
} while (sTags.indexOf (root.getName()) == -1);
return root;
}
/**
* Inserts all HTML tags required to build an ordered/unordered list at the caret's current position.
* If the aim is instead to turn the numbered/bulleted paragraphs into plain ones, it takes care of
* deleting the necessary tags.
* #param sTypeList Type of list to build: "ul" or "ol".
* #param textArea Editable area containing text.
*/
static void insertList (String sTypeList, JTextPane textArea) {
boolean bOnlyListSelected; //selection includes a list exclusively
int iStartIndex, iEndIndex, //element indexes included in selection
iStartSel, iEndSel, //starting and ending offset of selected text
iItemNo, //total number of list items
i;
String sHTML, //HTML code of text represented by a given element
sHTMLBlock, //HTML code block to be inserted into document model
sRest; //part of the text remaining unselected after the selected block
HTML.Tag tag; //current list tag
HTMLDocument document; //data model underlying the typed text
Element root, //root element of the document model tree
section; //element representing a block of text
SimpleAttributeSet attribIns; //backup of current input attributes
//Fetches the current document
document = (HTMLDocument) textArea.getDocument();
//Finds the topmost parent element of the current paragraph (effectively, is the list inside a table?)
root = getNearestParent (document, textArea.getCaretPosition(), "td body");
//Range of elements included in the selection
iStartSel = textArea.getSelectionStart();
iEndSel = textArea.getSelectionEnd();
iStartIndex = root.getElementIndex (iStartSel);
iEndIndex = root.getElementIndex (iEndSel);
//HTML-related initialisations
sHTML = "";
sHTMLBlock = "";
tag = null;
//Checks if selection is comprised of just list items
i = iStartIndex;
bOnlyListSelected = true;
do {
tag = HTML.getTag (root.getElement(i).getName());
//Is it a list tag?
if ((tag == null) || ((!tag.equals (HTML.Tag.OL)) && (!tag.equals (HTML.Tag.UL))))
bOnlyListSelected = false;
i++;
} while (bOnlyListSelected && (i <= iEndIndex));
//Back up current input attributes
attribIns = new SimpleAttributeSet (textArea.getInputAttributes());
try {
//At some point in the selection there is no previous list...
if (!bOnlyListSelected) {
//Inserts <LI> tags for every text block
for (i = iStartIndex; i <= iEndIndex; i++) {
section = root.getElement(i);
tag = HTML.getTag (section.getName());
//Retrieves current HTML
sHTML = extractHTML (section, new StringWriter());
//If it is non-listed text, reconstitute the paragraph
if (tag == null)
sHTML = "<p style=\"margin-top: 0;\">" +sHTML+ "</p>";
//Text in a list already => no nesting (delete <UL>/<OL> tags)
if (sHTML.indexOf("<li>") != -1) {
sHTML = sHTML.substring (sHTML.indexOf("<li>"), sHTML.length());
sHTML = sHTML.substring (0, sHTML.lastIndexOf("</li>") + 5);
//Non-listed text => add <LI> tags
} else sHTML = "<li>" +sHTML+ "</li>";
sHTMLBlock = sHTMLBlock + sHTML;
}
sHTMLBlock = "<"+sTypeList+">" +sHTMLBlock.trim()+ "</"+sTypeList+">";
//Gets the text coming after caret or end of selection
sRest = textArea.getText (iEndSel, document.getLength() - iEndSel);
//Adds an empty paragraph at the end of the list if the latter coincides with the end of the document
//or if the rest of the document is empty. This is to avoid a glitch in the editor kit's write() method.
//http://java-sl.com/tip_html_kit_last_empty_par.html
if ((root.getElement(iEndIndex).getEndOffset() == root.getEndOffset()) ||
sRest.replaceAll ("[\\p{Z}\\s]", "").trim().isEmpty())
sHTMLBlock = sHTMLBlock + sEmptyParagraph;
//Removes the remaining old non-listed text block and saves resulting HTML string to document model
document.setOuterHTML (root.getElement(iEndIndex), sHTMLBlock);
if (iEndIndex > iStartIndex)
document.remove (root.getElement(iStartIndex).getStartOffset(),
root.getElement(iEndIndex - 1).getEndOffset() -
root.getElement(iStartIndex).getStartOffset());
//Selection just includes list items
} else {
//Works out the list's length in terms of element indexes
root = root.getElement (root.getElementIndex (iStartSel));
iItemNo = root.getElementCount();
iStartIndex = root.getElementIndex (textArea.getSelectionStart());
iEndIndex = root.getElementIndex (textArea.getSelectionEnd());
//For everery <LI> block, remove the <LI> tag
for (i = iStartIndex; i <= iEndIndex; i++) {
sHTML = extractHTML (root.getElement(i), new StringWriter());
sHTML = sHTML.substring(sHTML.indexOf("<li>") + 4, sHTML.length());
sHTML = sHTML.substring(0, sHTML.lastIndexOf("</li>"));
sHTMLBlock = sHTMLBlock + sHTML;
}
//List selected partially? => divide list
if (iItemNo > (iEndIndex - iStartIndex + 1)) {
//Saves HTML string to document model
((HTMLEditorKit) textArea.getEditorKit()).insertHTML (document, root.getElement(iEndIndex).getEndOffset(),
sHTMLBlock, 3, 0, HTML.Tag.P);
//Removes the old block
document.remove (root.getElement(iStartIndex).getStartOffset(),
root.getElement(iEndIndex).getEndOffset() -
root.getElement(iStartIndex).getStartOffset());
//Removes the list tag associated with the block
} else document.setOuterHTML (root, sHTMLBlock.trim());
}
} catch (Exception eTexto) {
System.out.println ("Problemas al eliminar/insertar texto: " +eTexto);
eTexto.printStackTrace();
}
//Recover selection. Previous operations displace the cursor and thus selection highlight is lost
textArea.setSelectionStart (iStartSel);
textArea.setSelectionEnd (iEndSel);
//If only one list item has been created and is the first one, copy all previous style information to the list
if ((!bOnlyListSelected) && (iStartSel == iEndSel)) {
textArea.setCharacterAttributes (attribIns, false);
}
}