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.

CustomFont.java 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695
  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.fonts;
  19. import java.awt.Rectangle;
  20. import java.io.IOException;
  21. import java.io.InputStream;
  22. import java.net.URI;
  23. import java.util.ArrayList;
  24. import java.util.Collections;
  25. import java.util.HashMap;
  26. import java.util.HashSet;
  27. import java.util.List;
  28. import java.util.Map;
  29. import java.util.Set;
  30. import org.apache.fop.apps.io.InternalResourceResolver;
  31. import org.apache.fop.fonts.truetype.SVGGlyphData;
  32. /**
  33. * Abstract base class for custom fonts loaded from files, for example.
  34. */
  35. public abstract class CustomFont extends Typeface
  36. implements FontDescriptor, MutableFont {
  37. /** Fallback thickness for underline and strikeout when not provided by the font. */
  38. private static final int DEFAULT_LINE_THICKNESS = 50;
  39. private URI fontFileURI;
  40. private String fontName;
  41. private String fullName;
  42. private Set<String> familyNames;
  43. private String fontSubName;
  44. private URI embedFileURI;
  45. private String embedResourceName;
  46. private final InternalResourceResolver resourceResolver;
  47. private EmbeddingMode embeddingMode = EmbeddingMode.AUTO;
  48. private int capHeight;
  49. private int xHeight;
  50. private int ascender;
  51. private int descender;
  52. private int[] fontBBox = {0, 0, 0, 0};
  53. private int flags = 4;
  54. private int weight; //0 means unknown weight
  55. private int stemV;
  56. private int italicAngle;
  57. private int missingWidth;
  58. private FontType fontType = FontType.TYPE1;
  59. private int firstChar;
  60. private int lastChar = 255;
  61. private int underlinePosition;
  62. private int underlineThickness;
  63. private int strikeoutPosition;
  64. private int strikeoutThickness;
  65. private Map<Integer, Map<Integer, Integer>> kerning;
  66. private boolean useKerning = true;
  67. /** the character map, mapping Unicode ranges to glyph indices. */
  68. protected List<CMapSegment> cmap = new ArrayList<CMapSegment>();
  69. protected Map<Integer, SVGGlyphData> svgs;
  70. private boolean useAdvanced = true;
  71. private boolean simulateStyle;
  72. protected List<SimpleSingleByteEncoding> additionalEncodings;
  73. protected Map<Character, SingleByteFont.UnencodedCharacter> unencodedCharacters;
  74. /**
  75. * @param resourceResolver the URI resource resolver for controlling file access
  76. */
  77. public CustomFont(InternalResourceResolver resourceResolver) {
  78. this.resourceResolver = resourceResolver;
  79. }
  80. /** {@inheritDoc} */
  81. public URI getFontURI() {
  82. return fontFileURI;
  83. }
  84. /** {@inheritDoc} */
  85. public String getFontName() {
  86. return fontName;
  87. }
  88. /** {@inheritDoc} */
  89. public String getEmbedFontName() {
  90. return getFontName();
  91. }
  92. /** {@inheritDoc} */
  93. public String getFullName() {
  94. return fullName;
  95. }
  96. /**
  97. * Returns the font family names.
  98. * @return the font family names (a Set of Strings)
  99. */
  100. public Set<String> getFamilyNames() {
  101. return Collections.unmodifiableSet(this.familyNames);
  102. }
  103. /**
  104. * Returns the font family name stripped of whitespace.
  105. * @return the stripped font family
  106. * @see FontUtil#stripWhiteSpace(String)
  107. */
  108. public String getStrippedFontName() {
  109. return FontUtil.stripWhiteSpace(getFontName());
  110. }
  111. /**
  112. * Returns font's subfamily name.
  113. * @return the font's subfamily name
  114. */
  115. public String getFontSubName() {
  116. return fontSubName;
  117. }
  118. /**
  119. * Returns an URI representing an embeddable font file.
  120. *
  121. * @return URI to an embeddable font file or null if not available.
  122. */
  123. public URI getEmbedFileURI() {
  124. return embedFileURI;
  125. }
  126. /**
  127. * Returns the embedding mode for this font.
  128. * @return embedding mode
  129. */
  130. public EmbeddingMode getEmbeddingMode() {
  131. return embeddingMode;
  132. }
  133. /**
  134. * Returns an {@link InputStream} representing an embeddable font file.
  135. *
  136. * @return {@link InputStream} for an embeddable font file
  137. * @throws IOException if embedFileName is not null but Source is not found
  138. */
  139. public InputStream getInputStream() throws IOException {
  140. return resourceResolver.getResource(embedFileURI);
  141. }
  142. /**
  143. * Returns the lookup name to an embeddable font file available as a
  144. * resource.
  145. * (todo) Remove this method, this should be done using a resource: URI.
  146. * @return the lookup name
  147. */
  148. public String getEmbedResourceName() {
  149. return embedResourceName;
  150. }
  151. /**
  152. * {@inheritDoc}
  153. */
  154. public int getAscender() {
  155. return ascender;
  156. }
  157. /**
  158. * {@inheritDoc}
  159. */
  160. public int getDescender() {
  161. return descender;
  162. }
  163. /**
  164. * {@inheritDoc}
  165. */
  166. public int getCapHeight() {
  167. return capHeight;
  168. }
  169. /**
  170. * {@inheritDoc}
  171. */
  172. public int getAscender(int size) {
  173. return size * ascender;
  174. }
  175. /**
  176. * {@inheritDoc}
  177. */
  178. public int getDescender(int size) {
  179. return size * descender;
  180. }
  181. /**
  182. * {@inheritDoc}
  183. */
  184. public int getCapHeight(int size) {
  185. return size * capHeight;
  186. }
  187. /**
  188. * {@inheritDoc}
  189. */
  190. public int getXHeight(int size) {
  191. return size * xHeight;
  192. }
  193. /**
  194. * {@inheritDoc}
  195. */
  196. public int[] getFontBBox() {
  197. return fontBBox;
  198. }
  199. /** {@inheritDoc} */
  200. public int getFlags() {
  201. return flags;
  202. }
  203. /** {@inheritDoc} */
  204. public boolean isSymbolicFont() {
  205. return ((getFlags() & 4) != 0) || "ZapfDingbatsEncoding".equals(getEncodingName());
  206. //Note: The check for ZapfDingbats is necessary as the PFM does not reliably indicate
  207. //if a font is symbolic.
  208. }
  209. /**
  210. * Returns the font weight (100, 200...800, 900). This value may be different from the
  211. * one that was actually used to register the font.
  212. * @return the font weight (or 0 if the font weight is unknown)
  213. */
  214. public int getWeight() {
  215. return this.weight;
  216. }
  217. /**
  218. * {@inheritDoc}
  219. */
  220. public int getStemV() {
  221. return stemV;
  222. }
  223. /**
  224. * {@inheritDoc}
  225. */
  226. public int getItalicAngle() {
  227. return italicAngle;
  228. }
  229. /**
  230. * Returns the width to be used when no width is available.
  231. * @return a character width
  232. */
  233. public int getMissingWidth() {
  234. return missingWidth;
  235. }
  236. /**
  237. * {@inheritDoc}
  238. */
  239. public FontType getFontType() {
  240. return fontType;
  241. }
  242. /**
  243. * Returns the index of the first character defined in this font.
  244. * @return the index of the first character
  245. */
  246. public int getFirstChar() {
  247. return firstChar;
  248. }
  249. /**
  250. * Returns the index of the last character defined in this font.
  251. * @return the index of the last character
  252. */
  253. public int getLastChar() {
  254. return lastChar;
  255. }
  256. /**
  257. * Used to determine if kerning is enabled.
  258. * @return True if kerning is enabled.
  259. */
  260. public boolean isKerningEnabled() {
  261. return useKerning;
  262. }
  263. /**
  264. * {@inheritDoc}
  265. */
  266. public final boolean hasKerningInfo() {
  267. return (isKerningEnabled() && (kerning != null) && !kerning.isEmpty());
  268. }
  269. /**
  270. * {@inheritDoc}
  271. */
  272. public final Map<Integer, Map<Integer, Integer>> getKerningInfo() {
  273. if (hasKerningInfo()) {
  274. return kerning;
  275. } else {
  276. return Collections.emptyMap();
  277. }
  278. }
  279. /**
  280. * Used to determine if advanced typographic features are enabled.
  281. * By default, this is false, but may be overridden by subclasses.
  282. * @return true if enabled.
  283. */
  284. public boolean isAdvancedEnabled() {
  285. return useAdvanced;
  286. }
  287. /* ---- MutableFont interface ---- */
  288. /** {@inheritDoc} */
  289. public void setFontURI(URI uri) {
  290. this.fontFileURI = uri;
  291. }
  292. /** {@inheritDoc} */
  293. public void setFontName(String name) {
  294. this.fontName = name;
  295. }
  296. /** {@inheritDoc} */
  297. public void setFullName(String name) {
  298. this.fullName = name;
  299. }
  300. /** {@inheritDoc} */
  301. public void setFamilyNames(Set<String> names) {
  302. this.familyNames = new HashSet<String>(names);
  303. }
  304. /**
  305. * Sets the font's subfamily name.
  306. * @param subFamilyName the subfamily name of the font
  307. */
  308. public void setFontSubFamilyName(String subFamilyName) {
  309. this.fontSubName = subFamilyName;
  310. }
  311. /**
  312. * {@inheritDoc}
  313. */
  314. public void setEmbedURI(URI path) {
  315. this.embedFileURI = path;
  316. }
  317. /**
  318. * {@inheritDoc}
  319. */
  320. public void setEmbedResourceName(String name) {
  321. this.embedResourceName = name;
  322. }
  323. /**
  324. * {@inheritDoc}
  325. */
  326. public void setEmbeddingMode(EmbeddingMode embeddingMode) {
  327. this.embeddingMode = embeddingMode;
  328. }
  329. /**
  330. * {@inheritDoc}
  331. */
  332. public void setCapHeight(int capHeight) {
  333. this.capHeight = capHeight;
  334. }
  335. /**
  336. * Returns the XHeight value of the font.
  337. * @param xHeight the XHeight value
  338. */
  339. public void setXHeight(int xHeight) {
  340. this.xHeight = xHeight;
  341. }
  342. /**
  343. * {@inheritDoc}
  344. */
  345. public void setAscender(int ascender) {
  346. this.ascender = ascender;
  347. }
  348. /**
  349. * {@inheritDoc}
  350. */
  351. public void setDescender(int descender) {
  352. this.descender = descender;
  353. }
  354. /**
  355. * {@inheritDoc}
  356. */
  357. public void setFontBBox(int[] bbox) {
  358. this.fontBBox = bbox;
  359. }
  360. /**
  361. * {@inheritDoc}
  362. */
  363. public void setFlags(int flags) {
  364. this.flags = flags;
  365. }
  366. /**
  367. * Sets the font weight. Valid values are 100, 200...800, 900.
  368. * @param weight the font weight
  369. */
  370. public void setWeight(int weight) {
  371. weight = (weight / 100) * 100;
  372. weight = Math.max(100, weight);
  373. weight = Math.min(900, weight);
  374. this.weight = weight;
  375. }
  376. /**
  377. * {@inheritDoc}
  378. */
  379. public void setStemV(int stemV) {
  380. this.stemV = stemV;
  381. }
  382. /**
  383. * {@inheritDoc}
  384. */
  385. public void setItalicAngle(int italicAngle) {
  386. this.italicAngle = italicAngle;
  387. }
  388. /**
  389. * {@inheritDoc}
  390. */
  391. public void setMissingWidth(int width) {
  392. this.missingWidth = width;
  393. }
  394. /**
  395. * {@inheritDoc}
  396. */
  397. public void setFontType(FontType fontType) {
  398. this.fontType = fontType;
  399. }
  400. /**
  401. * {@inheritDoc}
  402. */
  403. public void setFirstChar(int index) {
  404. this.firstChar = index;
  405. }
  406. /**
  407. * {@inheritDoc}
  408. */
  409. public void setLastChar(int index) {
  410. this.lastChar = index;
  411. }
  412. /**
  413. * {@inheritDoc}
  414. */
  415. public void setKerningEnabled(boolean enabled) {
  416. this.useKerning = enabled;
  417. }
  418. /**
  419. * {@inheritDoc}
  420. */
  421. public void setAdvancedEnabled(boolean enabled) {
  422. this.useAdvanced = enabled;
  423. }
  424. /**
  425. * {@inheritDoc}
  426. */
  427. public void setSimulateStyle(boolean enabled) {
  428. this.simulateStyle = enabled;
  429. }
  430. public boolean getSimulateStyle() {
  431. return this.simulateStyle;
  432. }
  433. /** {@inheritDoc} */
  434. public void putKerningEntry(Integer key, Map<Integer, Integer> value) {
  435. if (kerning == null) {
  436. kerning = new HashMap<Integer, Map<Integer, Integer>>();
  437. }
  438. this.kerning.put(key, value);
  439. }
  440. /**
  441. * Replaces the existing kerning map with a new one.
  442. * @param kerningMap the kerning map (the integers are
  443. * character codes)
  444. */
  445. public void replaceKerningMap(Map<Integer, Map<Integer, Integer>> kerningMap) {
  446. if (kerningMap == null) {
  447. this.kerning = Collections.emptyMap();
  448. } else {
  449. this.kerning = kerningMap;
  450. }
  451. }
  452. /**
  453. * Sets the character map for this font. It maps all available Unicode characters
  454. * to their glyph indices inside the font.
  455. * @param cmap the character map
  456. */
  457. public void setCMap(CMapSegment[] cmap) {
  458. this.cmap.clear();
  459. Collections.addAll(this.cmap, cmap);
  460. }
  461. /**
  462. * Returns the character map for this font. It maps all available Unicode characters
  463. * to their glyph indices inside the font.
  464. * @return the character map
  465. */
  466. public CMapSegment[] getCMap() {
  467. return cmap.toArray(new CMapSegment[cmap.size()]);
  468. }
  469. public int getUnderlinePosition(int size) {
  470. return (underlinePosition == 0)
  471. ? getDescender(size) / 2
  472. : size * underlinePosition;
  473. }
  474. public void setUnderlinePosition(int underlinePosition) {
  475. this.underlinePosition = underlinePosition;
  476. }
  477. public int getUnderlineThickness(int size) {
  478. return size * ((underlineThickness == 0) ? DEFAULT_LINE_THICKNESS : underlineThickness);
  479. }
  480. public void setUnderlineThickness(int underlineThickness) {
  481. this.underlineThickness = underlineThickness;
  482. }
  483. public int getStrikeoutPosition(int size) {
  484. return (strikeoutPosition == 0)
  485. ? getXHeight(size) / 2
  486. : size * strikeoutPosition;
  487. }
  488. public void setStrikeoutPosition(int strikeoutPosition) {
  489. this.strikeoutPosition = strikeoutPosition;
  490. }
  491. public int getStrikeoutThickness(int size) {
  492. return (strikeoutThickness == 0) ? getUnderlineThickness(size) : size * strikeoutThickness;
  493. }
  494. public void setStrikeoutThickness(int strikeoutThickness) {
  495. this.strikeoutThickness = strikeoutThickness;
  496. }
  497. /**
  498. * Returns a Map of used Glyphs.
  499. * @return Map Map of used Glyphs
  500. */
  501. public abstract Map<Integer, Integer> getUsedGlyphs();
  502. /**
  503. * Returns the character from it's original glyph index in the font
  504. * @param glyphIndex The original index of the character
  505. * @return The character
  506. */
  507. public abstract char getUnicodeFromGID(int glyphIndex);
  508. /**
  509. * Indicates whether the encoding has additional encodings besides the primary encoding.
  510. * @return true if there are additional encodings.
  511. */
  512. public boolean hasAdditionalEncodings() {
  513. return (this.additionalEncodings != null) && (this.additionalEncodings.size() > 0);
  514. }
  515. /**
  516. * Returns the number of additional encodings this single-byte font maintains.
  517. * @return the number of additional encodings
  518. */
  519. public int getAdditionalEncodingCount() {
  520. if (hasAdditionalEncodings()) {
  521. return this.additionalEncodings.size();
  522. } else {
  523. return 0;
  524. }
  525. }
  526. /**
  527. * Returns an additional encoding.
  528. * @param index the index of the additional encoding
  529. * @return the additional encoding
  530. * @throws IndexOutOfBoundsException if the index is out of bounds
  531. */
  532. public SimpleSingleByteEncoding getAdditionalEncoding(int index)
  533. throws IndexOutOfBoundsException {
  534. if (hasAdditionalEncodings()) {
  535. return this.additionalEncodings.get(index);
  536. } else {
  537. throw new IndexOutOfBoundsException("No additional encodings available");
  538. }
  539. }
  540. /**
  541. * Adds an unencoded character (one that is not supported by the primary encoding).
  542. * @param ch the named character
  543. * @param width the width of the character
  544. */
  545. public void addUnencodedCharacter(NamedCharacter ch, int width, Rectangle bbox) {
  546. if (this.unencodedCharacters == null) {
  547. this.unencodedCharacters = new HashMap<Character, SingleByteFont.UnencodedCharacter>();
  548. }
  549. if (ch.hasSingleUnicodeValue()) {
  550. SingleByteFont.UnencodedCharacter uc = new SingleByteFont.UnencodedCharacter(ch, width, bbox);
  551. this.unencodedCharacters.put(ch.getSingleUnicodeValue(), uc);
  552. } else {
  553. //Cannot deal with unicode sequences, so ignore this character
  554. }
  555. }
  556. /**
  557. * Adds a character to additional encodings
  558. * @param ch character to map
  559. */
  560. protected char mapUnencodedChar(char ch) {
  561. if (this.unencodedCharacters != null) {
  562. SingleByteFont.UnencodedCharacter unencoded = this.unencodedCharacters.get(ch);
  563. if (unencoded != null) {
  564. if (this.additionalEncodings == null) {
  565. this.additionalEncodings = new ArrayList<SimpleSingleByteEncoding>();
  566. }
  567. SimpleSingleByteEncoding encoding = null;
  568. char mappedStart = 0;
  569. int additionalsCount = this.additionalEncodings.size();
  570. for (int i = 0; i < additionalsCount; i++) {
  571. mappedStart += 256;
  572. encoding = getAdditionalEncoding(i);
  573. char alt = encoding.mapChar(ch);
  574. if (alt != 0) {
  575. return (char)(mappedStart + alt);
  576. }
  577. }
  578. if (encoding != null && encoding.isFull()) {
  579. encoding = null;
  580. }
  581. if (encoding == null) {
  582. encoding = new SimpleSingleByteEncoding(
  583. getFontName() + "EncodingSupp" + (additionalsCount + 1));
  584. this.additionalEncodings.add(encoding);
  585. mappedStart += 256;
  586. }
  587. return (char)(mappedStart + encoding.addCharacter(unencoded.getCharacter()));
  588. }
  589. }
  590. return 0;
  591. }
  592. public boolean hasSVG() {
  593. return svgs != null;
  594. }
  595. public void setSVG(Map<Integer, SVGGlyphData> svgs) {
  596. this.svgs = svgs;
  597. }
  598. }