OFDPageDrawer.java 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910
  1. package com.ymwk.icvp.pdfToOfdBao;
  2. import org.apache.commons.io.IOUtils;
  3. import org.apache.fontbox.ttf.CmapLookup;
  4. import org.apache.fontbox.ttf.TrueTypeFont;
  5. import org.apache.fontbox.util.BoundingBox;
  6. import org.apache.pdfbox.contentstream.PDFGraphicsStreamEngine;
  7. import org.apache.pdfbox.cos.COSName;
  8. import org.apache.pdfbox.pdmodel.PDPage;
  9. import org.apache.pdfbox.pdmodel.common.PDStream;
  10. import org.apache.pdfbox.pdmodel.font.*;
  11. import org.apache.pdfbox.pdmodel.graphics.color.PDColor;
  12. import org.apache.pdfbox.pdmodel.graphics.color.PDColorSpace;
  13. import org.apache.pdfbox.pdmodel.graphics.image.PDImage;
  14. import org.apache.pdfbox.pdmodel.graphics.state.PDGraphicsState;
  15. import org.apache.pdfbox.pdmodel.graphics.state.RenderingMode;
  16. import org.apache.pdfbox.text.TextPosition;
  17. import org.apache.pdfbox.util.Matrix;
  18. import org.apache.pdfbox.util.Vector;
  19. import org.bouncycastle.crypto.Digest;
  20. import org.bouncycastle.crypto.digests.MD5Digest;
  21. import org.dom4j.Element;
  22. import org.ofdrw.core.OFDElement;
  23. import org.ofdrw.core.basicStructure.pageObj.layer.CT_Layer;
  24. import org.ofdrw.core.basicStructure.pageObj.layer.block.ImageObject;
  25. import org.ofdrw.core.basicStructure.pageObj.layer.block.PathObject;
  26. import org.ofdrw.core.basicStructure.pageObj.layer.block.TextObject;
  27. import org.ofdrw.core.basicType.ST_Array;
  28. import org.ofdrw.core.basicType.ST_ID;
  29. import org.ofdrw.core.basicType.ST_RefID;
  30. import org.ofdrw.core.graph.pathObj.AbbreviatedData;
  31. import org.ofdrw.core.graph.pathObj.CT_Path;
  32. import org.ofdrw.core.pageDescription.clips.CT_Clip;
  33. import org.ofdrw.core.pageDescription.clips.Clips;
  34. import org.ofdrw.core.pageDescription.color.color.CT_Color;
  35. import org.ofdrw.core.text.CT_CGTransform;
  36. import org.ofdrw.core.text.TextCode;
  37. import org.slf4j.Logger;
  38. import org.slf4j.LoggerFactory;
  39. import javax.imageio.ImageIO;
  40. import java.awt.geom.*;
  41. import java.io.ByteArrayOutputStream;
  42. import java.io.IOException;
  43. import java.io.InputStream;
  44. import java.util.ArrayList;
  45. import java.util.List;
  46. public class OFDPageDrawer extends PDFGraphicsStreamEngine {
  47. private final Logger logger = LoggerFactory.getLogger(this.getClass());
  48. private GeneralPath linePath = new GeneralPath();
  49. private int clipWindingRule = -1;
  50. private OFDCreator ofdCreator;
  51. private CT_Layer ctLayer;
  52. private float scale;
  53. private PDPage page;
  54. private float PX2MM = 2.834175f;
  55. private int pageRotation;
  56. private Area lastClip;
  57. public CT_Layer getCtLayer() {
  58. // 获取时,从队列添加
  59. for (Element ele : queue) {
  60. ctLayer.add(ele);
  61. }
  62. return ctLayer;
  63. }
  64. protected OFDPageDrawer(int idx, PDPage page, OFDCreator ofdCreator, float scale) throws IOException {
  65. super(page);
  66. this.page = page;
  67. this.ofdCreator = ofdCreator;
  68. ctLayer = this.ofdCreator.createLayer();
  69. this.scale = scale;
  70. }
  71. public void drawPage() throws IOException {
  72. processPage(this.getPage());
  73. }
  74. @Override
  75. public void appendRectangle(Point2D p0, Point2D p1, Point2D p2, Point2D p3) throws IOException {
  76. linePath.moveTo((float) p0.getX(), (float) p0.getY());
  77. linePath.lineTo((float) p1.getX(), (float) p1.getY());
  78. linePath.lineTo((float) p2.getX(), (float) p2.getY());
  79. linePath.lineTo((float) p3.getX(), (float) p3.getY());
  80. linePath.closePath();
  81. }
  82. @Override
  83. public void drawImage(PDImage pdImage) throws IOException {
  84. ByteArrayOutputStream bosImage = new ByteArrayOutputStream();
  85. String suffix = "png";
  86. ImageIO.write(pdImage.getImage(), suffix, bosImage);
  87. String name = String.format("%s.%s", bcMD5(bosImage.toByteArray()), suffix);
  88. ofdCreator.putImage(name, bosImage.toByteArray(), suffix);
  89. Matrix ctmNew = this.getGraphicsState().getCurrentTransformationMatrix();
  90. float imageXScale = ctmNew.getScalingFactorX();
  91. float imageYScale = ctmNew.getScalingFactorY();
  92. double x = ctmNew.getTranslateX() * scale;
  93. double y = (page.getCropBox().getHeight() - ctmNew.getTranslateY() - imageYScale) * scale;
  94. double w = imageXScale * scale;
  95. double h = imageYScale * scale;
  96. ImageObject imageObject = new ImageObject(ofdCreator.getNextRid());
  97. imageObject.setBoundary(x, y, w, h);
  98. imageObject.setResourceID(new ST_RefID(ST_ID.getInstance(ofdCreator.getImageMap().get(name))));
  99. imageObject.setCTM(ST_Array.getInstance(String.format("%.0f 0 0 %.0f 0 0", w, h)));
  100. setImageClip(imageObject, x, y, w, h);
  101. ctLayer.add(imageObject);
  102. }
  103. private String getPointsFromClipArea(Area area, double x, double y, double w, double h) {
  104. String abbreviatedData = "";
  105. double[] coords = new double[6];
  106. for (PathIterator pi = area.getPathIterator(null);
  107. !pi.isDone();
  108. pi.next()) {
  109. switch (pi.currentSegment(coords)) {
  110. case PathIterator.SEG_MOVETO:
  111. abbreviatedData += String.format("M %.1f %.1f ", (coords[0] * scale - x) / w, ((page.getCropBox().getHeight() - coords[1]) * scale - y) / h);
  112. break;
  113. case PathIterator.SEG_LINETO:
  114. abbreviatedData += String.format("L %.1f %.1f ", (coords[0] * scale - x) / w, ((page.getCropBox().getHeight() - coords[1]) * scale - y) / h);
  115. break;
  116. case PathIterator.SEG_CUBICTO:
  117. abbreviatedData += String.format("B %.1f %.1f %.1f %.1f %.1f %.1f ", (coords[0] * scale - x) / w, ((page.getCropBox().getHeight() - coords[1]) * scale - y) / h, (coords[2] * scale - x) / w, ((page.getCropBox().getHeight() - coords[3]) * scale - y) / h, (coords[4] * scale - x) / w, ((page.getCropBox().getHeight() - coords[5]) * scale - y) / h);
  118. break;
  119. case PathIterator.SEG_CLOSE:
  120. abbreviatedData += " C";
  121. }
  122. }
  123. return abbreviatedData;
  124. }
  125. private void setImageClip(ImageObject imageObject, double ix, double iy, double iw, double ih) {
  126. Area clippingPath = this.getGraphicsState().getCurrentClippingPath();
  127. if (clippingPath != lastClip) {
  128. if (clippingPath.getPathIterator((AffineTransform) null).isDone()) {
  129. return;
  130. } else {
  131. double w = (clippingPath.getBounds().width * scale);
  132. double h = (clippingPath.getBounds().height * scale);
  133. double x = (clippingPath.getBounds().x * scale);
  134. double y = (page.getCropBox().getHeight() - clippingPath.getBounds().y - clippingPath.getBounds().height) * scale;
  135. x = Double.parseDouble(String.format("%.2f", x));
  136. y = Double.parseDouble(String.format("%.2f", y));
  137. w = Double.parseDouble(String.format("%.0f", w));
  138. h = Double.parseDouble(String.format("%.0f", h));
  139. double clipX = Math.abs(x - ix) / iw;
  140. double clipY = Math.abs(y - iy) / ih;
  141. double clipW = w / iw;
  142. double clipH = h / ih;
  143. lastClip = clippingPath;
  144. String abbreviatedData = getPointsFromClipArea(clippingPath, x, y, w, h);
  145. if (abbreviatedData.contains("B") || (clipX >= 0 && clipY >= 0 && (clipW >= 0 && clipW <= 1) && (clipH >= 0 && clipH <= 1))) {
  146. OFDElement abbreviatedDataEle = OFDElement.getInstance("AbbreviatedData");
  147. abbreviatedDataEle.setText(abbreviatedData);
  148. CT_Path ctPath = new CT_Path();
  149. ctPath.setFill(true);
  150. ctPath.setStroke(false);
  151. ctPath.setBoundary(clipX, clipY, clipW, clipH);
  152. ctPath.add(abbreviatedDataEle);
  153. org.ofdrw.core.pageDescription.clips.Area area = new org.ofdrw.core.pageDescription.clips.Area();
  154. area.add(ctPath);
  155. CT_Clip ct_clip = new CT_Clip();
  156. ct_clip.addArea(area);
  157. Clips clips = new Clips();
  158. clips.addClip(ct_clip);
  159. imageObject.setClips(clips);
  160. }
  161. }
  162. }
  163. }
  164. private String bcMD5(byte[] imageBytes) {
  165. Digest digest = new MD5Digest();
  166. digest.update(imageBytes, 0, imageBytes.length);
  167. byte[] md5Bytes = new byte[digest.getDigestSize()];
  168. digest.doFinal(md5Bytes, 0);
  169. return org.bouncycastle.util.encoders.Hex.toHexString(md5Bytes);
  170. }
  171. @Override
  172. public void clip(int windingRule) throws IOException {
  173. clipWindingRule = windingRule;
  174. }
  175. @Override
  176. public void moveTo(float x, float y) throws IOException {
  177. linePath.moveTo(x, y);
  178. }
  179. @Override
  180. public void lineTo(float x, float y) throws IOException {
  181. linePath.lineTo(x, y);
  182. }
  183. @Override
  184. public void curveTo(float x1, float y1, float x2, float y2, float x3, float y3) throws IOException {
  185. linePath.curveTo(x1, y1, x2, y2, x3, y3);
  186. }
  187. @Override
  188. public Point2D getCurrentPoint() throws IOException {
  189. return linePath.getCurrentPoint();
  190. }
  191. @Override
  192. public void closePath() throws IOException {
  193. linePath.closePath();
  194. }
  195. @Override
  196. public void endPath() throws IOException {
  197. if (clipWindingRule != -1) {
  198. linePath.setWindingRule(clipWindingRule);
  199. getGraphicsState().intersectClippingPath(linePath);
  200. clipWindingRule = -1;
  201. }
  202. linePath.reset();
  203. }
  204. @Override
  205. public void strokePath() throws IOException {
  206. ctLayer.add(getPathObject(false));
  207. linePath.reset();
  208. }
  209. @Override
  210. public void fillPath(int i) throws IOException {
  211. ctLayer.add(getPathObject(true));
  212. linePath.reset();
  213. }
  214. /**
  215. * 获取路径对象
  216. */
  217. private PathObject getPathObject(boolean fill) throws IOException {
  218. double x = 0, y = 0;
  219. float w = page.getCropBox().getWidth() * scale;
  220. float h = page.getCropBox().getHeight() * scale;
  221. double lineWidth = getGraphicsState().getLineWidth() * scale;
  222. PathObject path = new PathObject(ST_ID.getInstance(String.valueOf(ofdCreator.getNextRid())));
  223. path.setLineWidth(lineWidth);
  224. path.setBoundary(x, y, w, h);
  225. AbbreviatedData data = new AbbreviatedData();
  226. drawLine(linePath.getPathIterator(null), data, h);
  227. if (fill) {
  228. CT_Color nonStrokeColor = getNonStrokeColor();
  229. if (nonStrokeColor != null) {
  230. path.setFillColor(nonStrokeColor);
  231. path.setFill(true);
  232. path.setStroke(false);
  233. }
  234. } else {
  235. CT_Color strokeColor = getStrokeColor();
  236. if (strokeColor != null) {
  237. path.setStroke(true);
  238. path.setFill(false);
  239. path.setStrokeColor(strokeColor);
  240. } else {
  241. path.setStroke(false);
  242. }
  243. }
  244. path.setAbbreviatedData(data);
  245. return path;
  246. }
  247. /**
  248. * 获取非stroke颜色
  249. */
  250. private CT_Color getNonStrokeColor() throws IOException {
  251. PDColor color = getGraphicsState().getNonStrokingColor();
  252. if (color == null) {
  253. return null;
  254. }
  255. return getColor(color);
  256. }
  257. /**
  258. * 获取stroke颜色
  259. */
  260. private CT_Color getStrokeColor() throws IOException {
  261. PDColor color = getGraphicsState().getStrokingColor();
  262. if (color == null) {
  263. return null;
  264. }
  265. return getColor(color);
  266. }
  267. /**
  268. * 画线
  269. */
  270. private void drawLine(PathIterator iterator, AbbreviatedData data, float height) throws IOException {
  271. double[] coords = new double[6];
  272. while (!iterator.isDone()) {
  273. switch (iterator.currentSegment(coords)) {
  274. case PathIterator.SEG_MOVETO:
  275. data.moveTo(coords[0] * scale, height - coords[1] * scale);
  276. break;
  277. case PathIterator.SEG_LINETO:
  278. data.lineTo(coords[0] * scale, height - coords[1] * scale);
  279. break;
  280. case PathIterator.SEG_CUBICTO:
  281. data.B(coords[0] * scale, height - coords[1] * scale,
  282. coords[2] * scale, height - coords[3] * scale,
  283. coords[4] * scale, height - coords[5] * scale);
  284. break;
  285. case PathIterator.SEG_CLOSE:
  286. data.close();
  287. closePath();
  288. break;
  289. default:
  290. break;
  291. }
  292. iterator.next();
  293. }
  294. }
  295. @Override
  296. public void fillAndStrokePath(int windingRule) throws IOException {
  297. GeneralPath path = (GeneralPath) this.linePath.clone();
  298. this.fillPath(windingRule);
  299. this.linePath = path;
  300. this.strokePath();
  301. }
  302. @Override
  303. public void shadingFill(COSName cosName) throws IOException {
  304. }
  305. private TextObject $textObj = null;
  306. private TextCode $textCode = null;
  307. private Double $deltaX = 0d;
  308. private List<String> $deltaXList = null;
  309. private Double $deltaY = 0d;
  310. private List<String> $deltaYList = null;
  311. private StringBuffer $text = null;
  312. // 字符索引
  313. private int $stringIndex = 0;
  314. private List<String> $glyphs = null;
  315. private CmapLookup $cmap = null;
  316. private byte[] textByte;
  317. // 字体转换索引
  318. private List<String> $cgTransforms = null;
  319. /**
  320. * 元素队列
  321. */
  322. private List<Element> queue = new ArrayList<Element>();
  323. @Override
  324. protected void showText(byte[] string) throws IOException {
  325. // 初始化
  326. $stringIndex = 0;
  327. $textObj = null;
  328. $textCode = null;
  329. $deltaX = 0d;
  330. $deltaY = 0d;
  331. $text = new StringBuffer();
  332. $glyphs = new ArrayList<String>();
  333. $deltaXList = new ArrayList<String>();
  334. $deltaYList = new ArrayList<String>();
  335. $cgTransforms = new ArrayList<String>();
  336. $cmap = null;
  337. textByte = string;
  338. // 处理文字
  339. super.showText(string);
  340. // 完成添加文字
  341. String text = $text.toString();
  342. if (null == text || "".equals(text)) {
  343. return;
  344. }
  345. if ($glyphs != null && $glyphs.size() > 0) {
  346. // 添加字符变换
  347. addCGTransform(text);
  348. }
  349. if ($deltaXList != null && $deltaXList.size() > 0) {
  350. // 宽度根据所有字体累加
  351. Double deltaWidth = $textObj.getBoundary().getWidth();
  352. for (String w : $deltaXList) {
  353. deltaWidth += Double.valueOf(w);
  354. }
  355. $textObj.setBoundary($textObj.getBoundary().setWidth(deltaWidth));
  356. $textCode.setDeltaX(new ST_Array($deltaXList.toArray(new String[$deltaXList.size()])));
  357. }
  358. if ($deltaYList != null && $deltaYList.size() > 0) {
  359. // 高度根据所有字体累加
  360. Double deltaHeight = $textObj.getBoundary().getHeight();
  361. for (String h : $deltaYList) {
  362. deltaHeight += Double.valueOf(h);
  363. }
  364. $textObj.setBoundary($textObj.getBoundary().setHeight(deltaHeight));
  365. $textCode.setDeltaY(new ST_Array($deltaYList.toArray(new String[$deltaYList.size()])));
  366. }
  367. // 扩展
  368. drawTextParam();
  369. $textCode.setContent(text);
  370. $textObj.addTextCode($textCode);
  371. queue.add($textObj);
  372. }
  373. /**
  374. * Glyph bounding boxes.
  375. */
  376. @Override
  377. protected void showGlyph(Matrix textRenderingMatrix, PDFont font, int code,
  378. Vector displacement) throws IOException {
  379. String unicode = font.toUnicode(code);
  380. boolean isUnicode = false;
  381. if (null == unicode || "".equals(unicode)) {
  382. unicode = "¤";// 占位符
  383. isUnicode = true;
  384. }
  385. $cgTransforms.add($stringIndex + "-" + unicode.length());
  386. PDGraphicsState state = getGraphicsState();
  387. RenderingMode renderingMode = state.getTextState().getRenderingMode();
  388. Matrix ctm = state.getCurrentTransformationMatrix();
  389. float fontSize = state.getTextState().getFontSize();
  390. float horizontalScaling = state.getTextState().getHorizontalScaling()
  391. / 100f;
  392. Matrix textMatrix = getTextMatrix();
  393. double angle = getRotate(textRenderingMatrix);
  394. BoundingBox bbox = font.getBoundingBox();
  395. if (bbox.getLowerLeftY() < Short.MIN_VALUE) {
  396. // PDFBOX-2158 and PDFBOX-3130
  397. // files by Salmat eSolutions / ClibPDF Library
  398. bbox.setLowerLeftY(-(bbox.getLowerLeftY() + 65536));
  399. }
  400. // 1/2 the bbox is used as the height todo: why?
  401. float glyphHeight = bbox.getHeight() / 2;
  402. // sometimes the bbox has very high values, but CapHeight is OK
  403. PDFontDescriptor fontDescriptor = font.getFontDescriptor();
  404. if (fontDescriptor != null) {
  405. float capHeight = fontDescriptor.getCapHeight();
  406. if (Float.compare(capHeight, 0) != 0 && (capHeight < glyphHeight
  407. || Float.compare(glyphHeight, 0) == 0)) {
  408. glyphHeight = capHeight;
  409. }
  410. // PDFBOX-3464, PDFBOX-4480, PDFBOX-4553:
  411. // sometimes even CapHeight has very high value, but Ascent and
  412. // Descent are ok
  413. float ascent = fontDescriptor.getAscent();
  414. float descent = fontDescriptor.getDescent();
  415. if (capHeight > ascent && ascent > 0 && descent < 0
  416. && ((ascent - descent) / 2 < glyphHeight
  417. || Float.compare(glyphHeight, 0) == 0)) {
  418. glyphHeight = (ascent - descent) / 2;
  419. }
  420. }
  421. // transformPoint from glyph space -> text space
  422. float height;
  423. if (font instanceof PDType3Font) {
  424. height = font.getFontMatrix().transformPoint(0, glyphHeight).y;
  425. } else {
  426. height = glyphHeight / 1000;
  427. }
  428. float displacementX = displacement.getX();
  429. // the sorting algorithm is based on the width of the character. As the
  430. // displacement
  431. // for vertical characters doesn't provide any suitable value for it, we
  432. // have to
  433. // calculate our own
  434. TrueTypeFont ttf = null;
  435. if (font instanceof PDTrueTypeFont) {
  436. ttf = ((PDTrueTypeFont) font).getTrueTypeFont();
  437. } else if (font instanceof PDType0Font) {
  438. PDCIDFont cidFont = ((PDType0Font) font).getDescendantFont();
  439. if (cidFont instanceof PDCIDFontType2) {
  440. ttf = ((PDCIDFontType2) cidFont).getTrueTypeFont();
  441. }
  442. }
  443. if (font.isVertical()) {
  444. displacementX = font.getWidth(code) / 1000;
  445. // there may be an additional scaling factor for true type fonts
  446. if (ttf != null && ttf.getUnitsPerEm() != 1000) {
  447. displacementX *= 1000f / ttf.getUnitsPerEm();
  448. }
  449. }
  450. //
  451. // legacy calculations which were previously in PDFStreamEngine
  452. //
  453. // DO NOT USE THIS CODE UNLESS YOU ARE WORKING WITH PDFTextStripper.
  454. // THIS CODE IS DELIBERATELY INCORRECT
  455. //
  456. // (modified) combined displacement, this is calculated *without* taking
  457. // the character
  458. // spacing and word spacing into account, due to legacy code in
  459. // TextStripper
  460. float tx = displacementX * fontSize * horizontalScaling;
  461. float ty = displacement.getY() * fontSize;
  462. // (modified) combined displacement matrix
  463. Matrix td = Matrix.getTranslateInstance(tx, ty);
  464. // (modified) text rendering matrix
  465. Matrix nextTextRenderingMatrix = td.multiply(textMatrix).multiply(ctm); // text
  466. // space
  467. // ->
  468. // device
  469. // space
  470. float nextX = nextTextRenderingMatrix.getTranslateX();
  471. float nextY = nextTextRenderingMatrix.getTranslateY();
  472. float dxDisplay = nextX - textRenderingMatrix.getTranslateX();
  473. float dyDisplay = height * textRenderingMatrix.getScalingFactorY();
  474. float glyphSpaceToTextSpaceFactor = 1 / 1000f;
  475. if (font instanceof PDType3Font) {
  476. glyphSpaceToTextSpaceFactor = font.getFontMatrix().getScaleX();
  477. }
  478. float spaceWidthText = 0;
  479. try {
  480. spaceWidthText = font.getSpaceWidth() * glyphSpaceToTextSpaceFactor;
  481. } catch (Throwable exception) {
  482. logger.error(exception.getMessage(), exception);
  483. }
  484. if (spaceWidthText == 0) {
  485. spaceWidthText = font.getAverageFontWidth()
  486. * glyphSpaceToTextSpaceFactor;
  487. spaceWidthText *= .80f;
  488. }
  489. if (spaceWidthText == 0) {
  490. spaceWidthText = 1.0f; // if could not find font, use a generic
  491. // value
  492. }
  493. // the space width has to be transformed into display units
  494. float spaceWidthDisplay = spaceWidthText
  495. * textRenderingMatrix.getScalingFactorX();
  496. if (angle == 0d) {
  497. fontSize = textRenderingMatrix.getScaleY() / PX2MM;
  498. }
  499. // 修复字体水平旋转,大小为空
  500. if (fontSize == 0) {
  501. fontSize = dyDisplay / PX2MM;
  502. }
  503. TextPosition textPosition = new TextPosition(pageRotation,
  504. page.getCropBox().getWidth(), page.getCropBox().getHeight(), textRenderingMatrix,
  505. nextX, nextY, Math.abs(dyDisplay), dxDisplay,
  506. Math.abs(spaceWidthDisplay), unicode, new int[]{code}, font,
  507. fontSize, (int) (fontSize * textMatrix.getScalingFactorX()));
  508. String fontName = "";
  509. if (font.getFontDescriptor() != null) {
  510. fontName = font.getFontDescriptor().getFontFamily();
  511. if (null == fontName || "".equals(fontName)) {
  512. fontName = font.getName();
  513. }
  514. }
  515. boolean willBeSubset = font.isEmbedded() || font.willBeSubset();
  516. String text = unicode;
  517. $text.append(text);
  518. int fontInt = 1;
  519. if ($stringIndex == 0) {
  520. long textID = ofdCreator.getNextRid();
  521. $textObj = new TextObject(textID);
  522. String fontHash = "" + font.hashCode();
  523. String fontId = ofdCreator.getFontMap().get(fontHash);
  524. if (fontId == null) {
  525. byte[] fontBytes = writeFont(font);
  526. fontId = ofdCreator.putFont(fontHash, fontName, fontName, fontBytes, ".otf");
  527. }
  528. fontInt = Integer.valueOf(fontId);
  529. $textCode = new TextCode();
  530. AffineTransform transform = new AffineTransform();
  531. transform.rotate(Math.toRadians(angle), textPosition.getWidth(),
  532. textPosition.getHeight());
  533. if (angle == 0)
  534. {
  535. $textCode.setX(0d);
  536. $textCode.setY((double)fontSize);
  537. $textObj.setBoundary(
  538. textPosition.getX() / PX2MM,
  539. Math.abs(
  540. textPosition.getY() / PX2MM
  541. - fontSize),
  542. textRenderingMatrix.getScaleX()
  543. / PX2MM,
  544. dyDisplay).setFont(fontInt)
  545. .setSize((double)fontSize);
  546. }
  547. else
  548. {
  549. $textCode.setX(0d);
  550. $textCode.setY(0d);
  551. $textObj.setBoundary(0, 0, textPosition.getX(),
  552. textPosition.getY()).setFont(fontInt).setSize(fontSize / (double)PX2MM);
  553. }
  554. }
  555. Integer glyphId = code;
  556. if (font instanceof PDTrueTypeFont) {
  557. PDTrueTypeFont ttfFont = (PDTrueTypeFont) font;
  558. glyphId = ttfFont.codeToGID(code);
  559. } else if (font instanceof PDType1CFont) {
  560. PDType1CFont type1CFont = (PDType1CFont) font;
  561. String name = type1CFont.codeToName(code);
  562. glyphId = type1CFont.getCFFType1Font().nameToGID(name);
  563. }
  564. if ($stringIndex == 0 && ttf != null && willBeSubset) {
  565. try {
  566. $cmap = ttf.getUnicodeCmapLookup();
  567. } catch (Exception e) {
  568. //logger.warn("加载字体异常",e);
  569. }
  570. /**
  571. * 存在cmap,从cmap中获取glyphId,否则code即为glyphId<br>
  572. * 详见PDFDebugger查看pdf内部结构方法源码 <br>
  573. *
  574. * @link org.apache.pdfbox.debugger.PDFDebugger
  575. */
  576. if ($cmap != null && ttf != null && willBeSubset) {
  577. glyphId = $cmap.getGlyphId(text.codePointAt(0));
  578. }
  579. }
  580. // unicode编码字体
  581. if (isUnicode || glyphId == 0) {
  582. glyphId = code;
  583. }
  584. if (willBeSubset) {
  585. $glyphs.add("" + glyphId);
  586. }
  587. Double topLeftX = textPosition.getX() / (double) PX2MM;
  588. if ($stringIndex > 0 && $deltaX.compareTo(topLeftX) != 0) {
  589. Double w = topLeftX - $deltaX;
  590. $deltaXList.add("" + w);
  591. }
  592. $deltaX = topLeftX;
  593. Double topLeftY = textPosition.getY() / (double) PX2MM;
  594. if ($stringIndex > 0 && $deltaY.compareTo(topLeftY) != 0) {
  595. Double h = topLeftY - $deltaY;
  596. $deltaYList.add("" + h);
  597. }
  598. $deltaY = topLeftY;
  599. Float scaleX = 1f;
  600. Float scaleY = 1f;
  601. Float lineWidth = state.getLineWidth();
  602. // 粗体
  603. if (renderingMode.isStroke() && lineWidth != null) {
  604. // minimum line width as used by Adobe Reader
  605. if (lineWidth < 0.25) {
  606. lineWidth = 0.25f;
  607. }
  608. $textObj.setLineWidth(
  609. lineWidth.doubleValue() / PX2MM);
  610. }
  611. if (angle == 0d) {
  612. $textObj.setCTM(
  613. new ST_Array(scaleX, ctm.getShearX() / PX2MM,
  614. ctm.getShearY() / PX2MM, scaleY, 0, 0));
  615. } else {
  616. $textObj.setCTM(new ST_Array(
  617. nextTextRenderingMatrix.getScaleX(),
  618. nextTextRenderingMatrix.getShearX(),
  619. nextTextRenderingMatrix.getShearY(),
  620. nextTextRenderingMatrix.getScaleY(),
  621. textPosition.getX() / PX2MM,
  622. textPosition.getY() / PX2MM));
  623. }
  624. if (nextTextRenderingMatrix.getScaleX() != nextTextRenderingMatrix.getScaleY()) {
  625. $textObj.setHScale((double) nextTextRenderingMatrix.getScaleX());
  626. }
  627. $stringIndex++;
  628. }
  629. private byte[] writeFont(PDFont font) {
  630. byte[] fontBytes = null;
  631. InputStream is = null;
  632. try {
  633. if (font instanceof PDTrueTypeFont) {
  634. PDTrueTypeFont f = (PDTrueTypeFont) font;
  635. is = f.getTrueTypeFont().getOriginalData();
  636. } else if (font instanceof PDType0Font) {
  637. PDType0Font type0Font = (PDType0Font) font;
  638. if (type0Font.getDescendantFont() instanceof PDCIDFontType2) {
  639. PDCIDFontType2 ff = (PDCIDFontType2) type0Font
  640. .getDescendantFont();
  641. is = ff.getTrueTypeFont().getOriginalData();
  642. } else if (type0Font
  643. .getDescendantFont() instanceof PDCIDFontType0) {
  644. // a Type0 CIDFont contains CFF font
  645. PDCIDFontType0 cidType0Font = (PDCIDFontType0) type0Font
  646. .getDescendantFont();
  647. PDFontDescriptor s = cidType0Font.getFontDescriptor();
  648. PDStream s1 = s.getFontFile();
  649. if (s1 != null) {
  650. is = s1.createInputStream();
  651. }
  652. PDStream s2 = s.getFontFile2();
  653. if (s2 != null) {
  654. is = s2.createInputStream();
  655. }
  656. PDStream s3 = s.getFontFile3();
  657. if (s3 != null) {
  658. is = s3.createInputStream();
  659. }
  660. } else {
  661. PDType0Font f = (PDType0Font) font;
  662. PDFontDescriptor s = f.getFontDescriptor();
  663. PDStream s1 = s.getFontFile();
  664. if (s1 != null) {
  665. is = s1.createInputStream();
  666. }
  667. PDStream s2 = s.getFontFile2();
  668. if (s2 != null) {
  669. is = s2.createInputStream();
  670. }
  671. PDStream s3 = s.getFontFile3();
  672. if (s3 != null) {
  673. is = s3.createInputStream();
  674. }
  675. }
  676. } else if (font instanceof PDType1Font) {
  677. PDType1Font f = (PDType1Font) font;
  678. PDFontDescriptor s = f.getFontDescriptor();
  679. PDStream s1 = s.getFontFile();
  680. if (s1 != null) {
  681. is = s1.createInputStream();
  682. }
  683. PDStream s2 = s.getFontFile2();
  684. if (s2 != null) {
  685. is = s2.createInputStream();
  686. }
  687. PDStream s3 = s.getFontFile3();
  688. if (s3 != null) {
  689. is = s3.createInputStream();
  690. }
  691. } else if (font instanceof PDType1CFont) {
  692. PDType1CFont f = (PDType1CFont) font;
  693. PDFontDescriptor s = f.getFontDescriptor();
  694. PDStream s1 = s.getFontFile();
  695. if (s1 != null) {
  696. is = s1.createInputStream();
  697. }
  698. PDStream s2 = s.getFontFile2();
  699. if (s2 != null) {
  700. is = s2.createInputStream();
  701. }
  702. PDStream s3 = s.getFontFile3();
  703. if (s3 != null) {
  704. is = s3.createInputStream();
  705. }
  706. } else if (font instanceof PDType3Font) {
  707. PDType3Font f = (PDType3Font) font;
  708. PDFontDescriptor s = f.getFontDescriptor();
  709. PDStream s1 = s.getFontFile();
  710. if (s1 != null) {
  711. is = s1.createInputStream();
  712. }
  713. PDStream s2 = s.getFontFile2();
  714. if (s2 != null) {
  715. is = s2.createInputStream();
  716. }
  717. PDStream s3 = s.getFontFile3();
  718. if (s3 != null) {
  719. is = s3.createInputStream();
  720. }
  721. }
  722. if (is != null) {
  723. fontBytes = IOUtils.toByteArray(is);
  724. } else {
  725. logger.error("生成字体文件异常:" + font.getClass());
  726. }
  727. } catch (Exception e) {
  728. logger.error("生成字体文件异常:" + font.getName(), e);
  729. } finally {
  730. IOUtils.closeQuietly(is);
  731. }
  732. return fontBytes;
  733. }
  734. /**
  735. * 绘画文字扩展属性<br/>
  736. * <p>
  737. * 绘画文字扩展属性
  738. * </p>
  739. *
  740. * @throws IOException 异常
  741. */
  742. private void drawTextParam() throws IOException {
  743. PDGraphicsState state = getGraphicsState();
  744. RenderingMode renderingMode = state.getTextState().getRenderingMode();
  745. setColor($textObj, state.getNonStrokingColor(), true);
  746. if (renderingMode.isFill()) {
  747. $textObj.setFill(true);
  748. }
  749. setColor($textObj, state.getStrokingColor(), false);
  750. if (renderingMode.isStroke()) {
  751. $textObj.setStroke(true);
  752. }
  753. }
  754. /**
  755. * 计算旋转角度<br/>
  756. * <p>
  757. * 计算旋转角度
  758. * </p>
  759. *
  760. * @param textMatrix
  761. * @return
  762. */
  763. private double getRotate(Matrix textMatrix) {
  764. double angle = 180 * Math.atan2(Float.valueOf(textMatrix.getShearX()),
  765. Float.valueOf(textMatrix.getScaleY())) / Math.PI;
  766. if (angle < 0) {
  767. angle = 360 + angle;
  768. }
  769. return angle;
  770. }
  771. private void setColor(Object object, PDColor color,
  772. boolean noStroke) throws IOException {
  773. if (color == null) {
  774. return;
  775. }
  776. CT_Color ctColor = getColor(color);
  777. if (noStroke) {
  778. ctColor.setOFDName("FillColor");
  779. if (object instanceof TextObject) {
  780. TextObject o = (TextObject) object;
  781. o.setFillColor(ctColor);
  782. } else if (object instanceof PathObject) {
  783. PathObject o = (PathObject) object;
  784. o.setFillColor(ctColor);
  785. } else if (object instanceof CT_Path) {
  786. CT_Path o = (CT_Path) object;
  787. o.setFillColor(ctColor);
  788. }
  789. } else {
  790. ctColor.setOFDName("StrokeColor");
  791. if (object instanceof TextObject) {
  792. TextObject o = (TextObject) object;
  793. o.setStrokeColor(ctColor);
  794. } else if (object instanceof PathObject) {
  795. PathObject o = (PathObject) object;
  796. o.setStrokeColor(ctColor);
  797. } else if (object instanceof CT_Path) {
  798. CT_Path o = (CT_Path) object;
  799. o.setStrokeColor(ctColor);
  800. }
  801. }
  802. }
  803. private CT_Color getColor(PDColor color) throws IOException {
  804. CT_Color ctColor = new CT_Color();
  805. if (color != null) {
  806. PDColorSpace colorSpace = color.getColorSpace();
  807. float[] rgb = colorSpace.toRGB(color.getComponents());
  808. ctColor = CT_Color.rgb(toRgbNumber(rgb[0]), toRgbNumber(rgb[1]), toRgbNumber(rgb[2]));
  809. }
  810. return ctColor;
  811. }
  812. /**
  813. * 添加字符变换
  814. *
  815. * @param text
  816. */
  817. private void addCGTransform(String text) {
  818. int total = text.length();
  819. CT_CGTransform cgTransform = new CT_CGTransform();
  820. cgTransform.setCodePosition(0);
  821. cgTransform.setGlyphCount(total);
  822. cgTransform.setCodeCount(total);
  823. cgTransform.setGlyphs(new ST_Array($glyphs.toArray(new String[$glyphs.size()])));
  824. $textObj.addCGTransform(cgTransform);
  825. }
  826. /**
  827. * 转换为rgb
  828. */
  829. private int toRgbNumber(float color) {
  830. return color < 0 ? 0 : (int) ((color > 1 ? 1 : color) * 255);
  831. }
  832. }