1 /*
   2  * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.javafx.font.coretext;
  27 
  28 import com.sun.javafx.font.Disposer;
  29 import com.sun.javafx.font.DisposerRecord;
  30 import com.sun.javafx.font.FontStrikeDesc;
  31 import com.sun.javafx.font.PrismFontFile;
  32 import com.sun.javafx.font.PrismFontStrike;
  33 import com.sun.javafx.geom.Path2D;
  34 import com.sun.javafx.geom.transform.BaseTransform;
  35 
  36 class CTFontFile extends PrismFontFile {
  37 
  38     private final long cgFontRef;
  39     /* Transform used for outline and bounds */
  40     private final static CGAffineTransform tx = new CGAffineTransform();
  41     static {
  42         tx.a = 1;   /* scale x */
  43         tx.d = -1;  /* scale y */
  44     }
  45 
  46     private static class SelfDisposerRecord implements DisposerRecord {
  47         private long cgFontRef;
  48 
  49         SelfDisposerRecord(long cgFontRef) {
  50             this.cgFontRef = cgFontRef;
  51         }
  52 
  53         @Override
  54         public synchronized void dispose() {
  55             if (cgFontRef != 0) {
  56                 OS.CFRelease(cgFontRef);
  57                 cgFontRef = 0;
  58             }
  59         }
  60     }
  61 
  62     CTFontFile(String name, String filename, int fIndex, boolean register,
  63                boolean embedded, boolean copy, boolean tracked) throws Exception {
  64         super(name, filename, fIndex, register, embedded, copy, tracked);
  65 
  66         if (embedded) {
  67             cgFontRef = createCGFontForEmbeddedFont();
  68             Disposer.addRecord(this, new SelfDisposerRecord(cgFontRef));
  69         } else {
  70             cgFontRef = 0;
  71         }
  72     }
  73 
  74     public static boolean registerFont(String fontfile) {
  75         if (fontfile == null) return false;
  76         long alloc = OS.kCFAllocatorDefault();
  77         boolean result = false;
  78         long fileRef = OS.CFStringCreate(fontfile);
  79         if (fileRef != 0) {
  80             int pathStyle = OS.kCFURLPOSIXPathStyle;
  81             long urlRef = OS.CFURLCreateWithFileSystemPath(alloc, fileRef, pathStyle, false);
  82             if (urlRef != 0) {
  83                 int scope = OS.kCTFontManagerScopeProcess;
  84                 result = OS.CTFontManagerRegisterFontsForURL(urlRef, scope, 0);
  85                 OS.CFRelease(urlRef);
  86             }
  87             OS.CFRelease(fileRef);
  88         }
  89         return result;
  90     }
  91 
  92     private long createCGFontForEmbeddedFont() {
  93         long cgFontRef = 0;
  94         final long fileNameRef = OS.CFStringCreate(getFileName());
  95         if (fileNameRef != 0) {
  96             final long url = OS.CFURLCreateWithFileSystemPath(
  97                     OS.kCFAllocatorDefault(), fileNameRef,
  98                     OS.kCFURLPOSIXPathStyle, false);
  99             if (url != 0) {
 100                 final long dataProvider = OS.CGDataProviderCreateWithURL(url);
 101                 if (dataProvider != 0) {
 102                     cgFontRef = OS.CGFontCreateWithDataProvider(dataProvider);
 103                     OS.CFRelease(dataProvider);
 104                 }
 105                 OS.CFRelease(url);
 106             }
 107             OS.CFRelease(fileNameRef);
 108         }
 109         return cgFontRef;
 110     }
 111 
 112     long getCGFontRef() {
 113         return cgFontRef;
 114     }
 115 
 116     CGRect getBBox(int gc, float size) {
 117         CTFontStrike strike = (CTFontStrike)getStrike(size, BaseTransform.IDENTITY_TRANSFORM);
 118         long fontRef = strike.getFontRef();
 119         if (fontRef == 0) return null;
 120         long pathRef = OS.CTFontCreatePathForGlyph(fontRef, (short)gc, tx);
 121         if (pathRef == 0) return null;
 122         CGRect rect = OS.CGPathGetPathBoundingBox(pathRef);
 123         OS.CGPathRelease(pathRef);
 124         return rect;
 125     }
 126 
 127     Path2D getGlyphOutline(int gc, float size) {
 128         CTFontStrike strike = (CTFontStrike)getStrike(size, BaseTransform.IDENTITY_TRANSFORM);
 129         long fontRef = strike.getFontRef();
 130         if (fontRef == 0) return null;
 131         long pathRef = OS.CTFontCreatePathForGlyph(fontRef, (short)gc, tx);
 132         if (pathRef == 0) return null;
 133         Path2D path = OS.CGPathApply(pathRef);
 134         OS.CGPathRelease(pathRef);
 135         return path;
 136     }
 137 
 138     @Override protected int[] createGlyphBoundingBox(int gc) {
 139         float size = 12;
 140         CTFontStrike strike = (CTFontStrike)getStrike(size,
 141                                                       BaseTransform.IDENTITY_TRANSFORM);
 142 
 143         long fontRef = strike.getFontRef();
 144         if (fontRef == 0) return null;
 145         int[] bb = new int[4];
 146 
 147         /* For some reason CTFontGetBoundingRectsForGlyphs has poor performance.
 148          * The fix is to use the 'loca' and the 'glyf' tables to determine
 149          * the glyph bounding box (same as T2K). This implementation
 150          * uses native code to read these tables since they can be large.
 151          * In case it fails, or the font doesn't have a glyph table
 152          * (CFF fonts), then the bounds of the glyph outline is used instead.
 153          */
 154         if (!isCFF()) {
 155             short format = getIndexToLocFormat();
 156             if (OS.CTFontGetBoundingRectForGlyphUsingTables(fontRef, (short)gc, format, bb)) {
 157                 return bb;
 158             }
 159         }
 160         /* Note: not using tx here as the bounds need to be y up */
 161         long pathRef = OS.CTFontCreatePathForGlyph(fontRef, (short)gc, null);
 162         if (pathRef == 0) return null;
 163         CGRect rect = OS.CGPathGetPathBoundingBox(pathRef);
 164         OS.CGPathRelease(pathRef);
 165         float scale = getUnitsPerEm() / size;
 166         bb[0] = (int)(Math.round(rect.origin.x * scale));
 167         bb[1] = (int)(Math.round(rect.origin.y * scale));
 168         bb[2] = (int)(Math.round((rect.origin.x + rect.size.width) * scale));
 169         bb[3] = (int)(Math.round((rect.origin.y + rect.size.height) * scale));
 170         return bb;
 171     }
 172 
 173     @Override
 174     protected PrismFontStrike<CTFontFile> createStrike(float size,
 175             BaseTransform transform, int aaMode, FontStrikeDesc desc) {
 176         return new CTFontStrike(this, size, transform, aaMode, desc);
 177     }
 178 
 179 }