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.

HemfFont.java 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  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. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. ==================================================================== */
  15. package org.apache.poi.hemf.record.emf;
  16. import java.io.IOException;
  17. import java.nio.charset.StandardCharsets;
  18. import java.util.ArrayList;
  19. import java.util.List;
  20. import org.apache.poi.common.usermodel.fonts.FontCharset;
  21. import org.apache.poi.hwmf.record.HwmfFont;
  22. import org.apache.poi.util.LittleEndian;
  23. import org.apache.poi.util.LittleEndianConsts;
  24. import org.apache.poi.util.LittleEndianInputStream;
  25. public class HemfFont extends HwmfFont {
  26. private static final int LOGFONT_SIZE = 92;
  27. private static final int LOGFONTPANOSE_SIZE = 320;
  28. protected interface LogFontDetails {}
  29. protected static class LogFontExDv implements LogFontDetails {
  30. protected int[] designVector;
  31. @Override
  32. public String toString() {
  33. return "{ designVectorLen: " + (designVector == null ? 0 : designVector.length) + " }";
  34. }
  35. }
  36. protected static class LogFontPanose implements LogFontDetails {
  37. enum FamilyType {
  38. PAN_ANY,
  39. PAN_NO_FIT,
  40. PAN_FAMILY_TEXT_DISPLAY,
  41. PAN_FAMILY_SCRIPT,
  42. PAN_FAMILY_DECORATIVE,
  43. PAN_FAMILY_PICTORIAL
  44. }
  45. enum SerifType {
  46. PAN_ANY,
  47. PAN_NO_FIT,
  48. PAN_SERIF_COVE,
  49. PAN_SERIF_OBTUSE_COVE,
  50. PAN_SERIF_SQUARE_COVE,
  51. PAN_SERIF_OBTUSE_SQUARE_COVE,
  52. PAN_SERIF_SQUARE,
  53. PAN_SERIF_THIN,
  54. PAN_SERIF_BONE,
  55. PAN_SERIF_EXAGGERATED,
  56. PAN_SERIF_TRIANGLE,
  57. PAN_SERIF_NORMAL_SANS,
  58. PAN_SERIF_OBTUSE_SANS,
  59. PAN_SERIF_PERP_SANS,
  60. PAN_SERIF_FLARED,
  61. PAN_SERIF_ROUNDED
  62. }
  63. enum FontWeight {
  64. PAN_ANY,
  65. PAN_NO_FIT,
  66. PAN_WEIGHT_VERY_LIGHT,
  67. PAN_WEIGHT_LIGHT,
  68. PAN_WEIGHT_THIN,
  69. PAN_WEIGHT_BOOK,
  70. PAN_WEIGHT_MEDIUM,
  71. PAN_WEIGHT_DEMI,
  72. PAN_WEIGHT_BOLD,
  73. PAN_WEIGHT_HEAVY,
  74. PAN_WEIGHT_BLACK,
  75. PAN_WEIGHT_NORD
  76. }
  77. enum Proportion {
  78. PAN_ANY,
  79. PAN_NO_FIT,
  80. PAN_PROP_OLD_STYLE,
  81. PAN_PROP_MODERN,
  82. PAN_PROP_EVEN_WIDTH,
  83. PAN_PROP_EXPANDED,
  84. PAN_PROP_CONDENSED,
  85. PAN_PROP_VERY_EXPANDED,
  86. PAN_PROP_VERY_CONDENSED,
  87. PAN_PROP_MONOSPACED
  88. }
  89. enum Contrast {
  90. PAN_ANY,
  91. PAN_NO_FIT,
  92. PAN_CONTRAST_NONE,
  93. PAN_CONTRAST_VERY_LOW,
  94. PAN_CONTRAST_LOW,
  95. PAN_CONTRAST_MEDIUM_LOW,
  96. PAN_CONTRAST_MEDIUM,
  97. PAN_CONTRAST_MEDIUM_HIGH,
  98. PAN_CONTRAST_HIGH,
  99. PAN_CONTRAST_VERY_HIGH
  100. }
  101. enum StrokeVariation {
  102. PAN_ANY,
  103. PAN_NO_FIT,
  104. PAN_STROKE_GRADUAL_DIAG,
  105. PAN_STROKE_GRADUAL_TRAN,
  106. PAN_STROKE_GRADUAL_VERT,
  107. PAN_STROKE_GRADUAL_HORZ,
  108. PAN_STROKE_RAPID_VERT,
  109. PAN_STROKE_RAPID_HORZ,
  110. PAN_STROKE_INSTANT_VERT
  111. }
  112. enum ArmStyle {
  113. PAN_ANY,
  114. PAN_NO_FIT,
  115. PAN_STRAIGHT_ARMS_HORZ,
  116. PAN_STRAIGHT_ARMS_WEDGE,
  117. PAN_STRAIGHT_ARMS_VERT,
  118. PAN_STRAIGHT_ARMS_SINGLE_SERIF,
  119. PAN_STRAIGHT_ARMS_DOUBLE_SERIF,
  120. PAN_BENT_ARMS_HORZ,
  121. PAN_BENT_ARMS_WEDGE,
  122. PAN_BENT_ARMS_VERT,
  123. PAN_BENT_ARMS_SINGLE_SERIF,
  124. PAN_BENT_ARMS_DOUBLE_SERIF
  125. }
  126. enum Letterform {
  127. PAN_ANY,
  128. PAN_NO_FIT,
  129. PAN_LETT_NORMAL_CONTACT,
  130. PAN_LETT_NORMAL_WEIGHTED,
  131. PAN_LETT_NORMAL_BOXED,
  132. PAN_LETT_NORMAL_FLATTENED,
  133. PAN_LETT_NORMAL_ROUNDED,
  134. PAN_LETT_NORMAL_OFF_CENTER,
  135. PAN_LETT_NORMAL_SQUARE,
  136. PAN_LETT_OBLIQUE_CONTACT,
  137. PAN_LETT_OBLIQUE_WEIGHTED,
  138. PAN_LETT_OBLIQUE_BOXED,
  139. PAN_LETT_OBLIQUE_FLATTENED,
  140. PAN_LETT_OBLIQUE_ROUNDED,
  141. PAN_LETT_OBLIQUE_OFF_CENTER,
  142. PAN_LETT_OBLIQUE_SQUARE
  143. }
  144. enum MidLine {
  145. PAN_ANY,
  146. PAN_NO_FIT,
  147. PAN_MIDLINE_STANDARD_TRIMMED,
  148. PAN_MIDLINE_STANDARD_POINTED,
  149. PAN_MIDLINE_STANDARD_SERIFED,
  150. PAN_MIDLINE_HIGH_TRIMMED,
  151. PAN_MIDLINE_HIGH_POINTED,
  152. PAN_MIDLINE_HIGH_SERIFED,
  153. PAN_MIDLINE_CONSTANT_TRIMMED,
  154. PAN_MIDLINE_CONSTANT_POINTED,
  155. PAN_MIDLINE_CONSTANT_SERIFED,
  156. PAN_MIDLINE_LOW_TRIMMED,
  157. PAN_MIDLINE_LOW_POINTED,
  158. PAN_MIDLINE_LOW_SERIFED
  159. }
  160. enum XHeight {
  161. PAN_ANY,
  162. PAN_NO_FIT,
  163. PAN_XHEIGHT_CONSTANT_SMALL,
  164. PAN_XHEIGHT_CONSTANT_STD,
  165. PAN_XHEIGHT_CONSTANT_LARGE,
  166. PAN_XHEIGHT_DUCKING_SMALL,
  167. PAN_XHEIGHT_DUCKING_STD,
  168. PAN_XHEIGHT_DUCKING_LARGE
  169. }
  170. protected int styleSize;
  171. protected int vendorId;
  172. protected int culture;
  173. protected FamilyType familyType;
  174. protected SerifType serifStyle;
  175. protected FontWeight weight;
  176. protected Proportion proportion;
  177. protected Contrast contrast;
  178. protected StrokeVariation strokeVariation;
  179. protected ArmStyle armStyle;
  180. protected Letterform letterform;
  181. protected MidLine midLine;
  182. protected XHeight xHeight;
  183. @Override
  184. public String toString() {
  185. return
  186. "{ styleSize: " + styleSize +
  187. ", vendorId: " + vendorId +
  188. ", culture: " + culture +
  189. ", familyType: '" + familyType + "'" +
  190. ", serifStyle: '" + serifStyle + "'" +
  191. ", weight: '" + weight + "'" +
  192. ", proportion: '" + proportion + "'" +
  193. ", contrast: '" + contrast + "'" +
  194. ", strokeVariation: '" + strokeVariation + "'" +
  195. ", armStyle: '" + armStyle + "'" +
  196. ", letterform: '" + letterform + "'" +
  197. ", midLine: '" + midLine + "'" +
  198. ", xHeight: '" + xHeight + "'" +
  199. "}";
  200. }
  201. }
  202. protected String fullname;
  203. protected String style;
  204. protected String script;
  205. protected LogFontDetails details;
  206. @Override
  207. public int init(LittleEndianInputStream leis, long recordSize) throws IOException {
  208. // A 32-bit signed integer that specifies the height, in logical units, of the font's
  209. // character cell or character. The character height value, also known as the em size, is the
  210. // character cell height value minus the internal leading value. The font mapper SHOULD
  211. // interpret the value specified in the Height field in the following manner.
  212. //
  213. // 0x00000000 < value:
  214. // The font mapper transforms this value into device units and matches it against
  215. // the cell height of the available fonts.
  216. //
  217. // 0x00000000
  218. // The font mapper uses a default height value when it searches for a match.
  219. //
  220. // value < 0x00000000:
  221. // The font mapper transforms this value into device units and matches its
  222. // absolute value against the character height of the available fonts.
  223. //
  224. // For all height comparisons, the font mapper SHOULD look for the largest font that does not
  225. // exceed the requested size.
  226. height = leis.readInt();
  227. // A 32-bit signed integer that specifies the average width, in logical units, of
  228. // characters in the font. If the Width field value is zero, an appropriate value SHOULD be
  229. // calculated from other LogFont values to find a font that has the typographer's intended
  230. // aspect ratio.
  231. width = leis.readInt();
  232. // A 32-bit signed integer that specifies the angle, in tenths of degrees,
  233. // between the escapement vector and the x-axis of the device. The escapement vector is
  234. // parallel to the baseline of a row of text.
  235. //
  236. // When the graphics mode is set to GM_ADVANCED, the escapement angle of the string can
  237. // be specified independently of the orientation angle of the string's characters.
  238. escapement = leis.readInt();
  239. // A 32-bit signed integer that specifies the angle, in tenths of degrees,
  240. // between each character's baseline and the x-axis of the device.
  241. orientation = leis.readInt();
  242. // A 32-bit signed integer that specifies the weight of the font in the range zero through 1000.
  243. // For example, 400 is normal and 700 is bold. If this value is zero, a default weight can be used.
  244. weight = leis.readInt();
  245. // An 8-bit unsigned integer that specifies an italic font if set to 0x01;
  246. // otherwise, it MUST be set to 0x00.
  247. italic = (leis.readUByte() != 0x00);
  248. // An 8-bit unsigned integer that specifies an underlined font if set to 0x01;
  249. // otherwise, it MUST be set to 0x00.
  250. underline = (leis.readUByte() != 0x00);
  251. // An 8-bit unsigned integer that specifies a strikeout font if set to 0x01;
  252. // otherwise, it MUST be set to 0x00.
  253. strikeOut = (leis.readUByte() != 0x00);
  254. // An 8-bit unsigned integer that specifies the set of character glyphs.
  255. // It MUST be a value in the WMF CharacterSet enumeration.
  256. // If the character set is unknown, metafile processing SHOULD NOT attempt
  257. // to translate or interpret strings that are rendered with that font.
  258. // If a typeface name is specified in the Facename field, the CharSet field
  259. // value MUST match the character set of that typeface.
  260. charSet = FontCharset.valueOf(leis.readUByte());
  261. // An 8-bit unsigned integer that specifies the output precision.
  262. // The output precision defines how closely the font is required to match the requested height, width,
  263. // character orientation, escapement, pitch, and font type.
  264. // It MUST be a value from the WMF OutPrecision enumeration.
  265. // Applications can use the output precision to control how the font mapper chooses a font when the
  266. // operating system contains more than one font with a specified name. For example, if an operating
  267. // system contains a font named Symbol in rasterized and TrueType forms, an output precision value
  268. // of OUT_TT_PRECIS forces the font mapper to choose the TrueType version.
  269. // A value of OUT_TT_ONLY_PRECIS forces the font mapper to choose a TrueType font, even if it is
  270. // necessary to substitute a TrueType font with another name.
  271. outPrecision = WmfOutPrecision.valueOf(leis.readUByte());
  272. // An 8-bit unsigned integer that specifies the clipping precision.
  273. // The clipping precision defines how to clip characters that are partially outside the clipping region.
  274. // It can be one or more of the WMF ClipPrecision Flags
  275. clipPrecision.init(leis);
  276. // An 8-bit unsigned integer that specifies the output quality. The output quality defines how closely
  277. // to attempt to match the logical-font attributes to those of an actual physical font.
  278. // It MUST be one of the values in the WMF FontQuality enumeration
  279. quality = WmfFontQuality.valueOf(leis.readUByte());
  280. // A WMF PitchAndFamily object that specifies the pitch and family of the font.
  281. // Font families describe the look of a font in a general way.
  282. // They are intended for specifying a font when the specified typeface is not available.
  283. pitchAndFamily = leis.readUByte();
  284. int size = 5* LittleEndianConsts.INT_SIZE+8*LittleEndianConsts.BYTE_SIZE;
  285. StringBuilder sb = new StringBuilder();
  286. // A string of no more than 32 Unicode characters that specifies the typeface name of the font.
  287. // If the length of this string is less than 32 characters, a terminating NULL MUST be present,
  288. // after which the remainder of this field MUST be ignored.
  289. int readBytes = readString(leis, sb, 32);
  290. if (readBytes == -1) {
  291. throw new IOException("Font facename can't be determined.");
  292. }
  293. facename = sb.toString();
  294. size += readBytes;
  295. if (recordSize <= LOGFONT_SIZE) {
  296. return size;
  297. }
  298. // A string of 64 Unicode characters that contains the font's full name.
  299. // Ifthe length of this string is less than 64 characters, a terminating
  300. // NULL MUST be present, after which the remainder of this field MUST be ignored.
  301. readBytes = readString(leis, sb, 64);
  302. if (readBytes == -1) {
  303. throw new IOException("Font fullname can't be determined.");
  304. }
  305. fullname = sb.toString();
  306. size += readBytes;
  307. // A string of 32 Unicode characters that defines the font's style. If the length of
  308. // this string is less than 32 characters, a terminating NULL MUST be present,
  309. // after which the remainder of this field MUST be ignored.
  310. readBytes = readString(leis, sb, 32);
  311. if (readBytes == -1) {
  312. throw new IOException("Font style can't be determined.");
  313. }
  314. style = sb.toString();
  315. size += readBytes;
  316. if (recordSize == LOGFONTPANOSE_SIZE) {
  317. // LogFontPanose Object
  318. LogFontPanose logPan = new LogFontPanose();
  319. details = logPan;
  320. int version = leis.readInt();
  321. // A 32-bit unsigned integer that specifies the point size at which font
  322. //hinting is performed. If set to zero, font hinting is performed at the point size corresponding
  323. //to the Height field in the LogFont object in the LogFont field.
  324. logPan.styleSize = (int)leis.readUInt();
  325. int match = leis.readInt();
  326. int reserved = leis.readInt();
  327. logPan.vendorId = leis.readInt();
  328. logPan.culture = leis.readInt();
  329. // An 8-bit unsigned integer that specifies the family type.
  330. // The value MUST be in the FamilyType enumeration table.
  331. logPan.familyType = LogFontPanose.FamilyType.values()[leis.readUByte()];
  332. // An 8-bit unsigned integer that specifies the serif style.
  333. // The value MUST be in the SerifType enumeration table.
  334. logPan.serifStyle = LogFontPanose.SerifType.values()[leis.readUByte()];
  335. // An 8-bit unsigned integer that specifies the weight of the font.
  336. // The value MUST be in the Weight enumeration table.
  337. logPan.weight = LogFontPanose.FontWeight.values()[leis.readUByte()];
  338. // An 8-bit unsigned integer that specifies the proportion of the font.
  339. // The value MUST be in the Proportion enumeration table.
  340. logPan.proportion = LogFontPanose.Proportion.values()[leis.readUByte()];
  341. // An 8-bit unsigned integer that specifies the proportion of the font.
  342. // The value MUST be in the Proportion enumeration table.
  343. logPan.contrast = LogFontPanose.Contrast.values()[leis.readUByte()];
  344. // An 8-bit unsigned integer that specifies the stroke variation for the font.
  345. // The value MUST be in the StrokeVariation enumeration table.
  346. logPan.strokeVariation = LogFontPanose.StrokeVariation.values()[leis.readUByte()];
  347. // An 8-bit unsigned integer that specifies the arm style of the font.
  348. // The value MUST be in the ArmStyle enumeration table.
  349. logPan.armStyle = LogFontPanose.ArmStyle.values()[leis.readUByte()];
  350. // An 8-bit unsigned integer that specifies the letterform of the font.
  351. // The value MUST be in the Letterform enumeration table.
  352. logPan.letterform = LogFontPanose.Letterform.values()[leis.readUByte()];
  353. // An 8-bit unsigned integer that specifies the midline of the font.
  354. // The value MUST be in the MidLine enumeration table.
  355. logPan.midLine = LogFontPanose.MidLine.values()[leis.readUByte()];
  356. // An 8-bit unsigned integer that specifies the x height of the font.
  357. // The value MUST be in the XHeight enumeration table.
  358. logPan.xHeight = LogFontPanose.XHeight.values()[leis.readUByte()];
  359. // skip 2 byte to ensure 32-bit alignment of this structure.
  360. leis.skip(2);
  361. size += 6*LittleEndianConsts.INT_SIZE+10* LittleEndianConsts.BYTE_SIZE+2;
  362. } else {
  363. // LogFontExDv Object
  364. LogFontExDv logEx = new LogFontExDv();
  365. details = logEx;
  366. // A string of 32 Unicode characters that defines the character set of the font.
  367. // If the length of this string is less than 32 characters, a terminating NULL MUST be present,
  368. // after which the remainder of this field MUST be ignored.
  369. readBytes = readString(leis, sb, 32);
  370. if (readBytes == -1) {
  371. throw new IOException("Font script can't be determined.");
  372. }
  373. script = sb.toString();
  374. size += readBytes;
  375. // Design Vector
  376. // A 32-bit unsigned integer that MUST be set to the value 0x08007664.
  377. int signature = leis.readInt();
  378. // some non-conformant applications don't write the magic code in
  379. // assert (signature == 0x08007664);
  380. // A 32-bit unsigned integer that specifies the number of elements in the
  381. // Values array. It MUST be in the range 0 to 16, inclusive.
  382. int numAxes = leis.readInt();
  383. assert (0 <= numAxes && numAxes <= 16);
  384. // An optional array of 32-bit signed integers that specify the values of the font axes of a
  385. // multiple master, OpenType font. The maximum number of values in the array is 16.
  386. if (numAxes > 0) {
  387. logEx.designVector = new int[numAxes];
  388. for (int i=0; i<numAxes; i++) {
  389. logEx.designVector[i] = leis.readInt();
  390. }
  391. }
  392. size += (2+numAxes)*LittleEndianConsts.INT_SIZE;
  393. }
  394. return size;
  395. }
  396. @Override
  397. public String toString() {
  398. return
  399. "{ fullname: '" + (fullname == null ? "" : fullname) + "'" +
  400. ", style: '" + (style == null ? "" : style) + "'" +
  401. ", script: '" + (script == null ? "" : script) + "'" +
  402. ", details: " + details +
  403. "," + super.toString().substring(1);
  404. }
  405. @Override
  406. protected int readString(LittleEndianInputStream leis, StringBuilder sb, int limit) throws IOException {
  407. sb.setLength(0);
  408. byte buf[] = new byte[limit*2];
  409. leis.readFully(buf);
  410. int b1, b2, readBytes = 0;
  411. do {
  412. if (readBytes == limit*2) {
  413. return -1;
  414. }
  415. b1 = buf[readBytes++];
  416. b2 = buf[readBytes++];
  417. } while ((b1 != 0 || b2 != 0) && b1 != -1 && b2 != -1 && readBytes <= limit*2);
  418. sb.append(new String(buf, 0, readBytes-2, StandardCharsets.UTF_16LE));
  419. return limit*2;
  420. }
  421. }