/*******************************************************************************
* Copyright 2010 Simon Mieth
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.kabeja.entities.util;
import java.util.Stack;
import java.util.StringTokenizer;
import org.kabeja.entities.MText;
import org.kabeja.entities.Text;
/**
* @author <a href="mailto:simon.mieth@gmx.de>Simon Mieth</a>
*
*/
public class TextParser {
public static TextDocument parseMText(MText text) {
// initialize
TextDocument doc = new TextDocument();
StringBuffer buf = new StringBuffer();
StringBuffer value = new StringBuffer();
StyledTextParagraph p = new StyledTextParagraph();
p.setFontHeight(text.getHeight());
p.setInsertPoint(text.getInsertPoint());
switch (text.getAlignment()) {
case MText.ATTACHMENT_TOP_LEFT:
p.setValign(StyledTextParagraph.VERTICAL_ALIGNMENT_TOP);
break;
case MText.ATTACHMENT_TOP_CENTER:
p.setValign(StyledTextParagraph.VERTICAL_ALIGNMENT_TOP);
break;
case MText.ATTACHMENT_TOP_RIGHT:
p.setValign(StyledTextParagraph.VERTICAL_ALIGNMENT_TOP);
break;
case MText.ATTACHMENT_MIDDLE_LEFT:
p.setValign(StyledTextParagraph.VERTICAL_ALIGNMENT_CENTER);
break;
case MText.ATTACHMENT_MIDDLE_CENTER:
p.setValign(StyledTextParagraph.VERTICAL_ALIGNMENT_CENTER);
break;
case MText.ATTACHMENT_MIDDLE_RIGHT:
p.setValign(StyledTextParagraph.VERTICAL_ALIGNMENT_CENTER);
break;
case MText.ATTACHMENT_BOTTOM_LEFT:
p.setValign(StyledTextParagraph.VERTICAL_ALIGNMENT_BASELINE);
break;
case MText.ATTACHMENT_BOTTOM_CENTER:
p.setValign(StyledTextParagraph.VERTICAL_ALIGNMENT_BASELINE);
break;
case MText.ATTACHMENT_BOTTOM_RIGHT:
p.setValign(StyledTextParagraph.VERTICAL_ALIGNMENT_BASELINE);
break;
default:
p.setValign(StyledTextParagraph.VERTICAL_ALIGNMENT_BASELINE);
break;
}
boolean formatting = false;
boolean keyfollow = false;
boolean complete = true;
Stack paras = new Stack();
int linecount = 0;
String str = text.getText();
char key = ' ';
// parse the symbols
str = parseSymbols(str);
// parse the style
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
switch (c) {
case '\\':
if (formatting) {
if (!complete) {
parseStyledTextParagraphSettings(key, value.toString(),
p);
value.delete(0, value.length());
formatting = true;
keyfollow = true;
} else {
buf.append(c);
formatting = false;
}
} else {
formatting = true;
keyfollow = true;
complete = false;
}
break;
case '~':
if (formatting) {
buf.append(c);
formatting = false;
keyfollow = false;
}
break;
case ';':
if (formatting) {
parseStyledTextParagraphSettings(key, value.toString(), p);
value.delete(0, value.length());
formatting = false;
complete = true;
keyfollow = false;
} else {
buf.append(c);
}
break;
case '}':
if (formatting && keyfollow) {
buf.append(c);
formatting = false;
keyfollow = false;
} else {
if (formatting) {
value.append(c);
} else {
// format change end
doc.addStyledParagraph(p);
p = createParagraphFromParent(p);
if (paras.size() > 0) {
p = (StyledTextParagraph) paras.pop();
}
}
}
break;
case '{':
if (formatting && keyfollow) {
buf.append(c);
formatting = false;
keyfollow = false;
} else {
if (formatting) {
value.append(c);
} else {
// start format change
if (i != 0) {
paras.add(p);
p = new StyledTextParagraph();
}
}
}
break;
case 'O':
if (formatting && keyfollow) {
p.setText(buf.toString());
buf.delete(0, buf.length());
doc.addStyledParagraph(p);
p = createParagraphFromParent(p);
p.setOverline(true);
formatting = false;
keyfollow = false;
} else {
if (formatting) {
value.append(c);
} else {
buf.append(c);
}
}
break;
case 'o':
if (formatting && keyfollow) {
p.setText(buf.toString());
buf.delete(0, buf.length());
doc.addStyledParagraph(p);
p = createParagraphFromParent(p);
p.setOverline(false);
formatting = false;
keyfollow = false;
} else {
if (formatting) {
value.append(c);
} else {
buf.append(c);
}
}
break;
case 'u':
if (formatting && keyfollow) {
p.setText(buf.toString());
buf.delete(0, buf.length());
doc.addStyledParagraph(p);
p = createParagraphFromParent(p);
p.setUnderline(false);
formatting = false;
keyfollow = false;
} else {
if (formatting) {
value.append(c);
} else {
buf.append(c);
}
}
break;
case 'L':
if (formatting && keyfollow) {
p.setText(buf.toString());
buf.delete(0, buf.length());
doc.addStyledParagraph(p);
p = createParagraphFromParent(p);
p.setUnderline(true);
formatting = false;
keyfollow = false;
} else {
if (formatting) {
value.append(c);
} else {
buf.append(c);
}
}
break;
case 'l':
if (formatting && keyfollow) {
p.setText(buf.toString());
buf.delete(0, buf.length());
doc.addStyledParagraph(p);
p = createParagraphFromParent(p);
p.setUnderline(false);
formatting = false;
keyfollow = false;
} else {
if (formatting) {
value.append(c);
} else {
buf.append(c);
}
}
break;
case 'P':
if (formatting && keyfollow) {
linecount++;
p.setText(buf.toString());
buf.delete(0, buf.length());
doc.addStyledParagraph(p);
p = createParagraphFromParent(p);
formatting = false;
keyfollow = false;
p.setLineIndex(linecount);
p.setNewline(true);
} else {
if (formatting) {
value.append(c);
} else {
buf.append(c);
}
}
break;
default:
if (formatting) {
if (keyfollow) {
key = c;
keyfollow = false;
} else {
value.append(c);
}
} else {
buf.append(c);
}
break;
}
}
if (formatting) {
parseStyledTextParagraphSettings(key, value.toString(), p);
}
if (buf.length() > 0) {
p.setText(buf.toString());
doc.addStyledParagraph(p);
}
if (doc.getParagraphCount() == 0) {
doc.addStyledParagraph(p);
}
return doc;
}
protected static StyledTextParagraph createParagraphFromParent(
StyledTextParagraph parent) {
StyledTextParagraph p = new StyledTextParagraph();
p.setValign(parent.getValign());
p.setBold(parent.isBold());
p.setFont(parent.getFont());
p.setItalic(parent.isItalic());
p.setUnderline(parent.isUnderline());
p.setOverline(parent.isOverline());
p.setWidth(parent.getWidth());
p.setFontHeight(parent.getFontHeight());
p.setInsertPoint(parent.getInsertPoint());
return p;
}
public static TextDocument parseText(Text text) {
TextDocument doc = new TextDocument();
// boolean asciicontrol = false;
StringBuffer buf = new StringBuffer();
StyledTextParagraph p = new StyledTextParagraph();
p.setFontHeight(text.getHeight());
p.setInsertPoint(text.getAlignmentPoint());
switch (text.getValign()) {
case Text.VALIGN_BASELINE:
if (text.getAlign() == Text.ALIGN_MIDDLE) {
// described in the DXF specs
p.setValign(StyledTextParagraph.VERTICAL_ALIGNMENT_CENTER);
} else {
p.setValign(StyledTextParagraph.VERTICAL_ALIGNMENT_BASELINE);
}
break;
case Text.VALIGN_BOTTOM:
p.setValign(StyledTextParagraph.VERTICAL_ALIGNMENT_BASELINE);
break;
case Text.VALIGN_CENTER:
p.setValign(StyledTextParagraph.VERTICAL_ALIGNMENT_CENTER);
break;
case Text.VALIGN_TOP:
p.setValign(StyledTextParagraph.VERTICAL_ALIGNMENT_TOP);
break;
}
if ((text.getAlign() == 3) || (text.getAlign() == 5)) {
double length = Utils.distance(text.getInsertPoint(), text
.getAlignmentPoint());
p.setWidth(length);
}
// parse the symbols
String str = parseSymbols(text.getText());
// parse the style
int marker = 0;
char c;
// initialize
boolean overline = false;
boolean underline = false;
for (int i = 0; i < str.length(); i++) {
c = str.charAt(i);
if (c == '%') {
marker++;
} else {
if (marker == 0) {
buf.append(c);
} else if (marker == 2) {
switch (c) {
case 'o':
p.setText(buf.toString());
p.setUnderline(underline);
p.setOverline(overline);
doc.addStyledParagraph(p);
p = createParagraphFromParent(p);
buf.delete(0, buf.length());
overline = !overline;
p.setOverline(overline);
break;
case 'u':
p.setText(buf.toString());
p.setUnderline(underline);
p.setOverline(overline);
doc.addStyledParagraph(p);
p = createParagraphFromParent(p);
buf.delete(0, buf.length());
underline = !underline;
p.setUnderline(underline);
break;
}
marker = 0;
}
}
}
if ((marker == 1) || (marker == 3)) {
buf.append('%');
}
// something left over?
if (buf.length() > 0) {
p.setText(buf.toString());
doc.addStyledParagraph(p);
}
return doc;
}
public static void parseStyledTextParagraphSettings(char key, String value,
StyledTextParagraph para) {
if (value.length() > 0) {
switch (key) {
case 'A':
para.setValign(Integer.parseInt(value));
break;
case 'H':
if (value.endsWith("x")) {
para.setFontHeight(para.getFontHeight()
* Double.parseDouble(value.substring(0, value
.length() - 1)));
} else {
para.setFontHeight(Double.parseDouble(value));
}
break;
case 'Q':
para.setObliquiAngle(Double.parseDouble(value));
break;
case 'W':
if (value.endsWith("x")) {
double widthFactor = Double.parseDouble(value.substring(0,
value.length() - 1));
para.setWidth(para.getWidth() * widthFactor);
} else {
para.setWidth(Double.parseDouble(value));
}
break;
case 'T':
para.setCharacterspace(Double.parseDouble(value));
break;
case 'f':
parseFontSettings(value.trim(), para);
break;
case 'F':
para.setFontFile(value.trim());
break;
case 'S':
// TODO handle
break;
}
}
}
public static void parseFontSettings(String value, StyledTextParagraph para) {
StringTokenizer st = new StringTokenizer(value, "|");
para.setFont(st.nextToken());
while (st.hasMoreTokens()) {
String option = st.nextToken();
char code = option.charAt(0);
int i = Integer.parseInt(option.substring(1));
switch (code) {
case 'b':
para.setBold(i == 1);
break;
case 'i':
para.setItalic(i == 1);
break;
case 'c':
// ??? color?
break;
case 'p':
// ???
break;
}
}
}
public static String parseSymbols(String text) {
boolean asciicontrol = false;
StringBuffer buf = new StringBuffer();
int marker = 0;
char c;
for (int i = 0; i < text.length(); i++) {
c = text.charAt(i);
if (c == '%') {
if (marker == 2) {
// a sequence of %%%%%065 means '%A'
buf.append('%');
marker = 0;
} else {
marker++;
}
} else if (c == '^') {
asciicontrol = true;
} else if (asciicontrol) {
// ASCII-control sign map
if (Character.isWhitespace(c)) {
buf.append('^');
} else {
// filtering acsii controls here
}
asciicontrol = false;
} else if (c == '\\') {
// test for unicode
if ((text.length() > (i + 6)) && (text.charAt(i + 1) == 'U')
&& (text.charAt(i + 2) == '+')) {
String part = text.substring(i + 3, i + 7);
int unicode = Integer.parseInt(part, 16);
buf.append((char) unicode);
i += 6;
} else {
buf.append('\\');
}
} else {
if (marker == 0) {
buf.append(c);
} else if (marker == 1) {
// the control % self
buf.append('%');
marker = 0;
} else if (marker == 2) {
switch (c) {
case 'd':
buf.append('\u00B0');
break;
case 'c':
buf.append('\u2205');
break;
case 'p':
buf.append('\u00B1');
break;
default:
if (Character.isDigit(c) && ((i + 2) < text.length())) {
String code = "" + c + text.charAt(i + 1)
+ text.charAt(i + 2);
try {
c = (char) Integer.parseInt(code);
buf.append(c);
i += 2;
} catch (NumberFormatException e) {
// TODO sometimes there is only one
// digit, so what should be the
// replacement???
buf.append('?');
i++;
}
} else {
// a style control write back
buf.append("%%");
buf.append(c);
}
}
marker = 0;
} else if (marker == 3) {
buf.append('%');
marker = 0;
}
}
}
if ((marker == 1) || (marker == 3)) {
buf.append('%');
}
return buf.toString();
}
}