You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PCLSoftFontManager.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /* $Id$ */
  18. package org.apache.fop.render.pcl.fonts;
  19. import java.io.ByteArrayOutputStream;
  20. import java.io.IOException;
  21. import java.util.ArrayList;
  22. import java.util.HashMap;
  23. import java.util.List;
  24. import java.util.Map;
  25. import java.util.Map.Entry;
  26. import org.apache.fop.fonts.CustomFont;
  27. import org.apache.fop.fonts.Typeface;
  28. import org.apache.fop.render.java2d.CustomFontMetricsMapper;
  29. public class PCLSoftFontManager {
  30. private ByteArrayOutputStream baos = new ByteArrayOutputStream();
  31. private PCLFontReader fontReader;
  32. private PCLByteWriterUtil pclByteWriter = new PCLByteWriterUtil();
  33. private List<PCLSoftFont> fonts = new ArrayList<PCLSoftFont>();
  34. private PCLFontReaderFactory fontReaderFactory;
  35. private static final int SOFT_FONT_SIZE = 255;
  36. public ByteArrayOutputStream makeSoftFont(Typeface font) throws IOException {
  37. List<Map<Character, Integer>> mappedGlyphs = mapFontGlyphs(font);
  38. if (fontReaderFactory == null) {
  39. fontReaderFactory = PCLFontReaderFactory.getInstance(pclByteWriter);
  40. }
  41. fontReader = fontReaderFactory.createInstance(font);
  42. initialize();
  43. if (mappedGlyphs.isEmpty()) {
  44. mappedGlyphs.add(new HashMap<Character, Integer>());
  45. }
  46. if (fontReader != null) {
  47. for (Map<Character, Integer> glyphSet : mappedGlyphs) {
  48. PCLSoftFont softFont = new PCLSoftFont(fonts.size() + 1, font,
  49. mappedGlyphs.get(0).size() != 0);
  50. softFont.setMappedChars(glyphSet);
  51. assignFontID();
  52. writeFontHeader(softFont.getMappedChars());
  53. softFont.setCharacterOffsets(fontReader.getCharacterOffsets());
  54. softFont.setOpenFont(fontReader.getFontFile());
  55. softFont.setReader(fontReader.getFontFileReader());
  56. fonts.add(softFont);
  57. }
  58. return baos;
  59. } else {
  60. return null;
  61. }
  62. }
  63. private List<Map<Character, Integer>> mapFontGlyphs(Typeface tf) {
  64. List<Map<Character, Integer>> mappedGlyphs = new ArrayList<Map<Character, Integer>>();
  65. if (tf instanceof CustomFontMetricsMapper) {
  66. CustomFontMetricsMapper fontMetrics = (CustomFontMetricsMapper) tf;
  67. CustomFont customFont = (CustomFont) fontMetrics.getRealFont();
  68. mappedGlyphs = mapGlyphs(customFont.getUsedGlyphs(), customFont);
  69. }
  70. return mappedGlyphs;
  71. }
  72. private List<Map<Character, Integer>> mapGlyphs(Map<Integer, Integer> usedGlyphs, CustomFont font) {
  73. int charCount = 32;
  74. List<Map<Character, Integer>> mappedGlyphs = new ArrayList<Map<Character, Integer>>();
  75. Map<Character, Integer> fontGlyphs = new HashMap<Character, Integer>();
  76. for (Entry<Integer, Integer> entry : usedGlyphs.entrySet()) {
  77. int glyphID = entry.getKey();
  78. if (glyphID == 0) {
  79. continue;
  80. }
  81. char unicode = font.getUnicodeFromGID(glyphID);
  82. if (charCount > SOFT_FONT_SIZE) {
  83. mappedGlyphs.add(fontGlyphs);
  84. charCount = 32;
  85. fontGlyphs = new HashMap<Character, Integer>();
  86. }
  87. fontGlyphs.put(unicode, charCount++);
  88. }
  89. if (fontGlyphs.size() > 0) {
  90. mappedGlyphs.add(fontGlyphs);
  91. }
  92. return mappedGlyphs;
  93. }
  94. private void initialize() {
  95. baos.reset();
  96. }
  97. private void assignFontID() throws IOException {
  98. baos.write(assignFontID(fonts.size() + 1));
  99. }
  100. public byte[] assignFontID(int fontID) throws IOException {
  101. return pclByteWriter.writeCommand(String.format("*c%dD", fontID));
  102. }
  103. private void writeFontHeader(Map<Character, Integer> mappedGlyphs) throws IOException {
  104. ByteArrayOutputStream header = new ByteArrayOutputStream();
  105. header.write(pclByteWriter.unsignedInt(fontReader.getDescriptorSize()));
  106. header.write(pclByteWriter.unsignedByte(fontReader.getHeaderFormat()));
  107. header.write(pclByteWriter.unsignedByte(fontReader.getFontType()));
  108. header.write(pclByteWriter.unsignedByte(fontReader.getStyleMSB()));
  109. header.write(0); // Reserved
  110. header.write(pclByteWriter.unsignedInt(fontReader.getBaselinePosition()));
  111. header.write(pclByteWriter.unsignedInt(fontReader.getCellWidth()));
  112. header.write(pclByteWriter.unsignedInt(fontReader.getCellHeight()));
  113. header.write(pclByteWriter.unsignedByte(fontReader.getOrientation()));
  114. header.write(fontReader.getSpacing());
  115. header.write(pclByteWriter.unsignedInt(fontReader.getSymbolSet()));
  116. header.write(pclByteWriter.unsignedInt(fontReader.getPitch()));
  117. header.write(pclByteWriter.unsignedInt(fontReader.getHeight()));
  118. header.write(pclByteWriter.unsignedInt(fontReader.getXHeight()));
  119. header.write(pclByteWriter.signedByte(fontReader.getWidthType()));
  120. header.write(pclByteWriter.unsignedByte(fontReader.getStyleLSB()));
  121. header.write(pclByteWriter.signedByte(fontReader.getStrokeWeight()));
  122. header.write(pclByteWriter.unsignedByte(fontReader.getTypefaceLSB()));
  123. header.write(pclByteWriter.unsignedByte(fontReader.getTypefaceMSB()));
  124. header.write(pclByteWriter.unsignedByte(fontReader.getSerifStyle()));
  125. header.write(pclByteWriter.unsignedByte(fontReader.getQuality()));
  126. header.write(pclByteWriter.signedByte(fontReader.getPlacement()));
  127. header.write(pclByteWriter.signedByte(fontReader.getUnderlinePosition()));
  128. header.write(pclByteWriter.unsignedByte(fontReader.getUnderlineThickness()));
  129. header.write(pclByteWriter.unsignedInt(fontReader.getTextHeight()));
  130. header.write(pclByteWriter.unsignedInt(fontReader.getTextWidth()));
  131. header.write(pclByteWriter.unsignedInt(fontReader.getFirstCode()));
  132. header.write(pclByteWriter.unsignedInt(fontReader.getLastCode()));
  133. header.write(pclByteWriter.unsignedByte(fontReader.getPitchExtended()));
  134. header.write(pclByteWriter.unsignedByte(fontReader.getHeightExtended()));
  135. header.write(pclByteWriter.unsignedInt(fontReader.getCapHeight()));
  136. header.write(pclByteWriter.unsignedLongInt(fontReader.getFontNumber()));
  137. header.write(pclByteWriter.padBytes(fontReader.getFontName().getBytes("US-ASCII"), 16, 32));
  138. header.write(pclByteWriter.unsignedInt(fontReader.getScaleFactor()));
  139. header.write(pclByteWriter.signedInt(fontReader.getMasterUnderlinePosition()));
  140. header.write(pclByteWriter.unsignedInt(fontReader.getMasterUnderlineThickness()));
  141. header.write(pclByteWriter.unsignedByte(fontReader.getFontScalingTechnology()));
  142. header.write(pclByteWriter.unsignedByte(fontReader.getVariety()));
  143. writeSegmentedFontData(header, mappedGlyphs);
  144. baos.write(getFontHeaderCommand(header.size()));
  145. baos.write(header.toByteArray());
  146. }
  147. private void writeSegmentedFontData(ByteArrayOutputStream header,
  148. Map<Character, Integer> mappedGlyphs) throws IOException {
  149. List<PCLFontSegment> fontSegments = fontReader.getFontSegments(mappedGlyphs);
  150. for (PCLFontSegment segment : fontSegments) {
  151. writeFontSegment(header, segment);
  152. }
  153. header.write(0); // Reserved
  154. // Checksum must equal 0 when added to byte 64 offset (modulo 256)
  155. long sum = 0;
  156. byte[] headerBytes = header.toByteArray();
  157. for (int i = 64; i < headerBytes.length; i++) {
  158. sum += headerBytes[i];
  159. }
  160. int remainder = (int) (sum % 256);
  161. header.write(256 - remainder);
  162. }
  163. private byte[] getFontHeaderCommand(int headerSize) throws IOException {
  164. return pclByteWriter.writeCommand(String.format(")s%dW", headerSize));
  165. }
  166. private void writeFontSegment(ByteArrayOutputStream header, PCLFontSegment segment) throws IOException {
  167. header.write(pclByteWriter.unsignedInt(segment.getIdentifier().getValue()));
  168. header.write(pclByteWriter.unsignedInt(segment.getData().length));
  169. header.write(segment.getData());
  170. }
  171. /**
  172. * Finds a soft font associated with the given typeface. If more than one instance of the font exists (as each font
  173. * is bound and restricted to 255 characters) it will find the last font with available capacity.
  174. * @param font The typeface associated with the soft font
  175. * @return Returns the PCLSoftFont with available capacity
  176. */
  177. public PCLSoftFont getSoftFont(Typeface font, String text) {
  178. for (PCLSoftFont sftFont : fonts) {
  179. if (sftFont.getTypeface().equals(font)
  180. && sftFont.getCharCount() + countNonMatches(sftFont, text) < SOFT_FONT_SIZE) {
  181. return sftFont;
  182. }
  183. }
  184. return null;
  185. }
  186. public PCLSoftFont getSoftFontFromID(int index) {
  187. return fonts.get(index - 1);
  188. }
  189. private int countNonMatches(PCLSoftFont font, String text) {
  190. int result = 0;
  191. for (char ch : text.toCharArray()) {
  192. int value = font.getUnicodeCodePoint(ch);
  193. if (value == -1) {
  194. result++;
  195. }
  196. }
  197. return result;
  198. }
  199. public int getSoftFontID(Typeface tf) throws IOException {
  200. PCLSoftFont font = getSoftFont(tf, "");
  201. for (int i = 0; i < fonts.size(); i++) {
  202. if (fonts.get(i).equals(font)) {
  203. return i + 1;
  204. }
  205. }
  206. return -1;
  207. }
  208. public List<PCLTextSegment> getTextSegments(String text, Typeface font) {
  209. List<PCLTextSegment> textSegments = new ArrayList<PCLTextSegment>();
  210. int curFontID = -1;
  211. String current = "";
  212. for (char ch : text.toCharArray()) {
  213. for (PCLSoftFont softFont : fonts) {
  214. if (curFontID == -1) {
  215. curFontID = softFont.getFontID();
  216. }
  217. if (softFont.getCharIndex(ch) == -1 || !softFont.getTypeface().equals(font)) {
  218. continue;
  219. }
  220. if (current.length() > 0 && curFontID != softFont.getFontID()) {
  221. textSegments.add(new PCLTextSegment(curFontID, current));
  222. current = "";
  223. curFontID = softFont.getFontID();
  224. }
  225. if (curFontID != softFont.getFontID()) {
  226. curFontID = softFont.getFontID();
  227. }
  228. current += ch;
  229. break;
  230. }
  231. }
  232. if (current.length() > 0) {
  233. textSegments.add(new PCLTextSegment(curFontID, current));
  234. }
  235. return textSegments;
  236. }
  237. public static class PCLTextSegment {
  238. private String text;
  239. private int fontID;
  240. public PCLTextSegment(int fontID, String text) {
  241. this.text = text;
  242. this.fontID = fontID;
  243. }
  244. public String getText() {
  245. return text;
  246. }
  247. public int getFontID() {
  248. return fontID;
  249. }
  250. }
  251. }