リビジョン | 13 (tree) |
---|---|
日時 | 2020-02-24 15:56:37 |
作者 | hirukawa_ryo |
* pdf-brewer 0.4
あふれた文字列の行末処理を指定する命令 \text-overflow を追加しました。wrap を指定すると従来通り行の折り返しをおこないます。truncate を指定すると折り返さずに残りの文字を切り捨てます。ellipsis を指定すると折り返さずに残りの文字を切り捨て行末に省略記号(…)を出力します。\text-overflow を指定しない場合の既定値は wrap です。
@@ -17,6 +17,7 @@ | ||
17 | 17 | import java.util.Map.Entry; |
18 | 18 | import java.util.Scanner; |
19 | 19 | |
20 | +import net.osdn.pdf_brewer.instruction.text.TextOverflow; | |
20 | 21 | import org.apache.pdfbox.pdmodel.common.PDRectangle; |
21 | 22 | |
22 | 23 | import net.osdn.pdf_brewer.instruction.Align; |
@@ -115,6 +116,8 @@ | ||
115 | 116 | } |
116 | 117 | } else if(first.equals("text-align")) { |
117 | 118 | instructions.add(new TextAlign(indent, tokens)); |
119 | + } else if(first.equals("text-overflow")) { | |
120 | + instructions.add(new TextOverflow(indent, tokens)); | |
118 | 121 | } |
119 | 122 | } catch(IllegalArgumentException e) { |
120 | 123 | e.printStackTrace(); |
@@ -22,6 +22,7 @@ | ||
22 | 22 | private float fontSize; |
23 | 23 | private float lineHeight; //行の高さ倍率 |
24 | 24 | private Horizontal textAlign; |
25 | + private Overflow textOverflow; | |
25 | 26 | |
26 | 27 | private float lineWidth; //RectやLineで線を描画するときの線の太さ |
27 | 28 | private int lineStyle; |
@@ -44,6 +45,7 @@ | ||
44 | 45 | fontSize = 14.0f; |
45 | 46 | lineHeight = 1.8f; |
46 | 47 | textAlign = Horizontal.Left; |
48 | + textOverflow = Overflow.Wrap; | |
47 | 49 | |
48 | 50 | lineWidth = LineStyle.WIDTH_MEDIUM; |
49 | 51 | lineStyle = LineStyle.LINE_SOLID; |
@@ -67,6 +69,7 @@ | ||
67 | 69 | fontSize = parent.getFontSize(); |
68 | 70 | lineHeight = parent.getLineHeight(); |
69 | 71 | textAlign = parent.getTextAlignment(); |
72 | + textOverflow = parent.getTextOverflow(); | |
70 | 73 | |
71 | 74 | lineWidth = parent.getLineWidth(); |
72 | 75 | lineStyle = parent.getLineStyle(); |
@@ -175,7 +178,15 @@ | ||
175 | 178 | public void setTextAlignment(Horizontal textAlign) { |
176 | 179 | this.textAlign = textAlign; |
177 | 180 | } |
178 | - | |
181 | + | |
182 | + public Overflow getTextOverflow() { | |
183 | + return textOverflow; | |
184 | + } | |
185 | + | |
186 | + public void setTextOverflow(Overflow textOverflow) { | |
187 | + this.textOverflow = textOverflow; | |
188 | + } | |
189 | + | |
179 | 190 | public float getLineWidth() { |
180 | 191 | return lineWidth; |
181 | 192 | } |
@@ -0,0 +1,7 @@ | ||
1 | +package net.osdn.pdf_brewer; | |
2 | + | |
3 | +public enum Overflow { | |
4 | + Wrap, | |
5 | + Truncate, | |
6 | + Ellipsis, | |
7 | +} |
@@ -4,6 +4,7 @@ | ||
4 | 4 | import java.util.ArrayList; |
5 | 5 | import java.util.List; |
6 | 6 | |
7 | +import net.osdn.pdf_brewer.instruction.text.TextOverflow; | |
7 | 8 | import org.apache.pdfbox.pdmodel.PDPageContentStream; |
8 | 9 | import org.apache.pdfbox.pdmodel.font.PDFont; |
9 | 10 |
@@ -47,10 +48,15 @@ | ||
47 | 48 | ops.add(new LineHeightOp(lineHeightInstruction.getLineHeight())); |
48 | 49 | } |
49 | 50 | } else if(instruction instanceof TextAlign) { |
50 | - TextAlign textAlignInstruction = (TextAlign)instruction; | |
51 | - if(textAlignInstruction.getTextAlignment() != null) { | |
51 | + TextAlign textAlignInstruction = (TextAlign) instruction; | |
52 | + if (textAlignInstruction.getTextAlignment() != null) { | |
52 | 53 | ops.add(new TextAlignOp(textAlignInstruction.getTextAlignment())); |
53 | 54 | } |
55 | + } else if(instruction instanceof TextOverflow) { | |
56 | + TextOverflow textOverflowInstruction = (TextOverflow)instruction; | |
57 | + if(textOverflowInstruction.getTextOverflow() != null) { | |
58 | + ops.add(new TextOverflowOp(textOverflowInstruction.getTextOverflow())); | |
59 | + } | |
54 | 60 | } else { |
55 | 61 | |
56 | 62 | } |
@@ -70,6 +76,10 @@ | ||
70 | 76 | if(textAlign == null) { |
71 | 77 | textAlign = Horizontal.Left; |
72 | 78 | } |
79 | + Overflow textOverflow = context.getTextOverflow(); | |
80 | + if(textOverflow == null) { | |
81 | + textOverflow = Overflow.Wrap; | |
82 | + } | |
73 | 83 | float fontSize = context.getFontSize(); |
74 | 84 | float lineHeight = context.getLineHeight(); |
75 | 85 | boolean isHeightChanged = true; |
@@ -110,14 +120,47 @@ | ||
110 | 120 | leading[lineNumber] = l; |
111 | 121 | } |
112 | 122 | } |
113 | - ops2.add(new TextOp(result.text1)); | |
114 | - lineWidth[lineNumber] += result.width; | |
115 | - rest -= result.width; | |
116 | 123 | if(result.text2 == null) { |
117 | 124 | // 改行の必要なくすべて出力可能。 |
125 | + ops2.add(new TextOp(result.text1)); | |
126 | + lineWidth[lineNumber] += result.width; | |
127 | + rest -= result.width; | |
128 | + | |
129 | + // 続く行(text2)が存在しないので処理を終了します。 | |
118 | 130 | text = null; |
131 | + } else if(textOverflow == Overflow.Truncate) { | |
132 | + // 切り捨て(Truncate)が指定されている。 | |
133 | + ops2.add(new TextOp(result.text1)); | |
134 | + lineWidth[lineNumber] += result.width; | |
135 | + rest -= result.width; | |
136 | + | |
137 | + // 続く行(text2)が残っていますが処理せずに終了します。(切り捨て) | |
138 | + text = null; | |
139 | + } else if(textOverflow == Overflow.Ellipsis) { | |
140 | + // 省略(Ellipsis)が指定されている。 | |
141 | + // 行末に省略記号を追加します。余白が不足している場合は、result.text1 の末尾から1文字ずつ削っていきます。 | |
142 | + String ellipsisChar = "\u2026"; | |
143 | + float ellipsisWidth = getStringWidth(font, fontSize, ellipsisChar); | |
144 | + while(result.text1.length() >= 1 && rest - result.width - ellipsisWidth < 0.0f) { | |
145 | + result.text1 = result.text1.substring(0, result.text1.length() - 1); | |
146 | + result.width = getStringWidth(font, fontSize, result.text1); | |
147 | + } | |
148 | + if(rest - result.width - ellipsisWidth >= 0.0f) { | |
149 | + result.text1 += ellipsisChar; | |
150 | + result.width = getStringWidth(font, fontSize, result.text1); | |
151 | + } | |
152 | + ops2.add(new TextOp(result.text1)); | |
153 | + lineWidth[lineNumber] += result.width; | |
154 | + rest -= result.width; | |
155 | + | |
156 | + // 続く行(text2)が残っていますが処理せずに終了します。(省略) | |
157 | + text = null; | |
119 | 158 | } else { |
120 | 159 | // テキストの一部を出力可能。残りを出力するために改行が必要。 |
160 | + ops2.add(new TextOp(result.text1)); | |
161 | + lineWidth[lineNumber] += result.width; | |
162 | + rest -= result.width; | |
163 | + | |
121 | 164 | float w = lineWidth[lineNumber]; |
122 | 165 | float h = lineNumber == 0 ? fontHeight[0] : leading[lineNumber]; |
123 | 166 | ops2.add(new NewLineOp(w, h)); |
@@ -149,11 +192,11 @@ | ||
149 | 192 | ops2.add(fontOp); |
150 | 193 | isHeightChanged = true; |
151 | 194 | } else if(op instanceof TextAlignOp) { |
152 | - TextAlignOp textAlignOp = (TextAlignOp)op; | |
153 | - if(textAlignOp.textAlign != null) { | |
154 | - if(textAlign != textAlignOp.textAlign) { | |
195 | + TextAlignOp textAlignOp = (TextAlignOp) op; | |
196 | + if (textAlignOp.textAlign != null) { | |
197 | + if (textAlign != textAlignOp.textAlign) { | |
155 | 198 | textAlign = textAlignOp.textAlign; |
156 | - if(lineWidth[lineNumber] > 0f) { | |
199 | + if (lineWidth[lineNumber] > 0f) { | |
157 | 200 | float w = lineWidth[lineNumber]; |
158 | 201 | float h = lineNumber == 0 ? fontHeight[0] : leading[lineNumber]; |
159 | 202 | ops2.add(new NewLineOp(w, h)); |
@@ -164,6 +207,12 @@ | ||
164 | 207 | } |
165 | 208 | ops2.add(textAlignOp); |
166 | 209 | } |
210 | + } else if(op instanceof TextOverflowOp) { | |
211 | + TextOverflowOp textOverflowOp = (TextOverflowOp)op; | |
212 | + if(textOverflowOp.textOverflow != null) { | |
213 | + textOverflow = textOverflowOp.textOverflow; | |
214 | + context.setTextOverflow(textOverflow); | |
215 | + } | |
167 | 216 | } else if(op instanceof LineHeightOp) { |
168 | 217 | LineHeightOp lineHeightOp = (LineHeightOp)op; |
169 | 218 | if(lineHeightOp.lineHeight != Float.NaN && lineHeightOp.lineHeight >= 0.0) { |
@@ -385,6 +434,14 @@ | ||
385 | 434 | this.textAlign = textAlign; |
386 | 435 | } |
387 | 436 | } |
437 | + | |
438 | + private static class TextOverflowOp extends Op { | |
439 | + public Overflow textOverflow; | |
440 | + | |
441 | + public TextOverflowOp(Overflow textOverflow) { | |
442 | + this.textOverflow = textOverflow; | |
443 | + } | |
444 | + } | |
388 | 445 | |
389 | 446 | private static class LineHeightOp extends Op { |
390 | 447 | public float lineHeight; |
@@ -0,0 +1,32 @@ | ||
1 | +package net.osdn.pdf_brewer.instruction.text; | |
2 | + | |
3 | +import net.osdn.pdf_brewer.Overflow; | |
4 | + | |
5 | +import java.util.List; | |
6 | + | |
7 | +public class TextOverflow extends TextBufferingInstruction { | |
8 | + | |
9 | + private Overflow textOverflow; | |
10 | + | |
11 | + public TextOverflow(int indent, List<Object> params) { | |
12 | + super(indent, params); | |
13 | + | |
14 | + for(int i = 0; i < params.size(); i++) { | |
15 | + Object obj = params.get(i); | |
16 | + if(obj instanceof String) { | |
17 | + String s = ((String)obj).toLowerCase(); | |
18 | + if(s.equals("wrap")) { | |
19 | + textOverflow = Overflow.Wrap; | |
20 | + } else if(s.equals("truncate")) { | |
21 | + textOverflow = Overflow.Truncate; | |
22 | + } else if(s.equals("ellipsis")) { | |
23 | + textOverflow = Overflow.Ellipsis; | |
24 | + } | |
25 | + } | |
26 | + } | |
27 | + } | |
28 | + | |
29 | + public Overflow getTextOverflow() { | |
30 | + return textOverflow; | |
31 | + } | |
32 | +} |