2018-02-19 20:57:08 +01:00
|
|
|
/*
|
|
|
|
* Copyright © 2018 Ebrahim Byagowi
|
2018-02-24 10:19:42 +01:00
|
|
|
* Copyright © 2018 Google, Inc.
|
2018-02-19 20:57:08 +01:00
|
|
|
*
|
|
|
|
* This is part of HarfBuzz, a text shaping library.
|
|
|
|
*
|
|
|
|
* Permission is hereby granted, without written agreement and without
|
|
|
|
* license or royalty fees, to use, copy, modify, and distribute this
|
|
|
|
* software and its documentation for any purpose, provided that the
|
|
|
|
* above copyright notice and the following two paragraphs appear in
|
|
|
|
* all copies of this software.
|
|
|
|
*
|
|
|
|
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
|
|
|
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
|
|
|
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
|
|
|
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
|
|
|
* DAMAGE.
|
|
|
|
*
|
|
|
|
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
|
|
|
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
|
|
|
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
|
|
|
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
|
|
|
*
|
|
|
|
* Google Author(s): Behdad Esfahbod
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef HB_AAT_LAYOUT_TRAK_TABLE_HH
|
|
|
|
#define HB_AAT_LAYOUT_TRAK_TABLE_HH
|
|
|
|
|
2018-08-26 07:36:36 +02:00
|
|
|
#include "hb-aat-layout-common.hh"
|
|
|
|
#include "hb-ot-layout.hh"
|
|
|
|
#include "hb-open-type.hh"
|
2018-02-19 20:57:08 +01:00
|
|
|
|
2018-04-12 11:08:19 +02:00
|
|
|
/*
|
|
|
|
* trak -- Tracking
|
|
|
|
* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6trak.html
|
|
|
|
*/
|
2018-03-26 09:34:30 +02:00
|
|
|
#define HB_AAT_TAG_trak HB_TAG('t','r','a','k')
|
2018-02-19 20:57:08 +01:00
|
|
|
|
|
|
|
|
|
|
|
namespace AAT {
|
|
|
|
|
|
|
|
|
|
|
|
struct TrackTableEntry
|
|
|
|
{
|
2018-04-18 09:39:37 +02:00
|
|
|
friend struct TrackData;
|
|
|
|
|
2018-12-17 19:01:01 +01:00
|
|
|
float get_track_value () const { return track.to_float (); }
|
2018-02-25 10:00:33 +01:00
|
|
|
|
2018-12-16 20:08:10 +01:00
|
|
|
int get_value (const void *base, unsigned int index,
|
|
|
|
unsigned int table_size) const
|
2018-12-12 12:44:37 +01:00
|
|
|
{ return (base+valuesZ).as_array (table_size)[index]; }
|
2018-02-19 20:57:08 +01:00
|
|
|
|
2018-10-11 16:18:46 +02:00
|
|
|
public:
|
2018-12-16 20:08:10 +01:00
|
|
|
bool sanitize (hb_sanitize_context_t *c, const void *base,
|
|
|
|
unsigned int table_size) const
|
2018-02-24 10:19:42 +01:00
|
|
|
{
|
2018-10-11 16:18:46 +02:00
|
|
|
TRACE_SANITIZE (this);
|
|
|
|
return_trace (likely (c->check_struct (this) &&
|
2018-12-12 12:44:37 +01:00
|
|
|
(valuesZ.sanitize (c, base, table_size))));
|
2018-02-24 10:19:42 +01:00
|
|
|
}
|
|
|
|
|
2018-02-19 20:57:08 +01:00
|
|
|
protected:
|
2020-04-24 21:15:17 +02:00
|
|
|
HBFixed track; /* Track value for this record. */
|
2018-10-11 16:18:46 +02:00
|
|
|
NameID trackNameID; /* The 'name' table index for this track.
|
|
|
|
* (a short word or phrase like "loose"
|
|
|
|
* or "very tight") */
|
2019-04-30 22:05:10 +02:00
|
|
|
NNOffsetTo<UnsizedArrayOf<FWORD>>
|
2018-04-18 09:39:37 +02:00
|
|
|
valuesZ; /* Offset from start of tracking table to
|
2018-03-14 16:31:53 +01:00
|
|
|
* per-size tracking values for this track. */
|
2018-02-19 20:57:08 +01:00
|
|
|
|
|
|
|
public:
|
2018-02-21 00:02:25 +01:00
|
|
|
DEFINE_SIZE_STATIC (8);
|
2018-02-19 20:57:08 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct TrackData
|
|
|
|
{
|
2018-12-16 20:08:10 +01:00
|
|
|
float interpolate_at (unsigned int idx,
|
|
|
|
float target_size,
|
|
|
|
const TrackTableEntry &trackTableEntry,
|
|
|
|
const void *base) const
|
2018-10-11 17:25:07 +02:00
|
|
|
{
|
|
|
|
unsigned int sizes = nSizes;
|
2019-09-10 19:31:07 +02:00
|
|
|
hb_array_t<const HBFixed> size_table ((base+sizeTable).arrayZ, sizes);
|
2018-10-11 17:25:07 +02:00
|
|
|
|
|
|
|
float s0 = size_table[idx].to_float ();
|
|
|
|
float s1 = size_table[idx + 1].to_float ();
|
|
|
|
float t = unlikely (s0 == s1) ? 0.f : (target_size - s0) / (s1 - s0);
|
2018-10-18 02:55:47 +02:00
|
|
|
return t * trackTableEntry.get_value (base, idx + 1, sizes) +
|
|
|
|
(1.f - t) * trackTableEntry.get_value (base, idx, sizes);
|
2018-10-11 17:25:07 +02:00
|
|
|
}
|
|
|
|
|
2018-12-16 20:08:10 +01:00
|
|
|
int get_tracking (const void *base, float ptem) const
|
2018-02-24 10:19:42 +01:00
|
|
|
{
|
2018-10-11 16:32:08 +02:00
|
|
|
/*
|
|
|
|
* Choose track.
|
|
|
|
*/
|
2018-02-25 10:00:33 +01:00
|
|
|
const TrackTableEntry *trackTableEntry = nullptr;
|
2018-10-11 16:30:32 +02:00
|
|
|
unsigned int count = nTracks;
|
|
|
|
for (unsigned int i = 0; i < count; i++)
|
2018-10-11 16:29:02 +02:00
|
|
|
{
|
|
|
|
/* Note: Seems like the track entries are sorted by values. But the
|
|
|
|
* spec doesn't explicitly say that. It just mentions it in the example. */
|
|
|
|
|
|
|
|
/* For now we only seek for track entries with zero tracking value */
|
|
|
|
|
2018-04-23 17:06:53 +02:00
|
|
|
if (trackTable[i].get_track_value () == 0.f)
|
2018-10-11 16:29:02 +02:00
|
|
|
{
|
2018-10-18 02:55:47 +02:00
|
|
|
trackTableEntry = &trackTable[i];
|
2018-10-11 16:29:02 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2018-02-25 10:00:33 +01:00
|
|
|
if (!trackTableEntry) return 0.;
|
2018-02-24 10:19:42 +01:00
|
|
|
|
2018-10-11 16:32:08 +02:00
|
|
|
/*
|
|
|
|
* Choose size.
|
|
|
|
*/
|
2018-10-11 16:30:32 +02:00
|
|
|
unsigned int sizes = nSizes;
|
2018-10-11 16:32:08 +02:00
|
|
|
if (!sizes) return 0.;
|
|
|
|
if (sizes == 1) return trackTableEntry->get_value (base, 0, sizes);
|
|
|
|
|
2019-09-10 19:31:07 +02:00
|
|
|
hb_array_t<const HBFixed> size_table ((base+sizeTable).arrayZ, sizes);
|
2018-10-11 17:25:07 +02:00
|
|
|
unsigned int size_index;
|
2018-10-19 20:00:20 +02:00
|
|
|
for (size_index = 0; size_index < sizes - 1; size_index++)
|
Remove assumption about Core Text working in 96 DPI
Core Text doesn't actually have a concept of DPI internally, as it
doesn't rasterize anything by itself, it just generates vector paths
that get passed along to Core Graphics.
In practice this means Core Text operates in the classical macOS
logical DPI of 72, with one typographic point corresponding to one
point in the Core Graphics coordinate system, which for a normal
bitmap context then corresponds to one pixel -- or two pixels for
a "retina" context with a 2x scale transform.
Scaling the font point sizes given to HarfBuzz to an assumed DPI
of 96 is problematic with this in mind, as fonts with optical
features such as 'trak' tables for tracking, or color glyphs,
will then base the metrics off of the wrong point size compared
to what the client asked for.
This in turn causes mismatches between the metrics of the shaped
text and the actual rasterization, which doesn't include the 72
to 96 DPI scaling.
If a 96 DPI is needed, such as on the Web, the scaling should be
done outside of HarfBuzz, allowing the client to keep the DPI of
the shaping in sync with the rasterization.
The recommended way to do that is by scaling the font point size,
not by applying a transform to the target Core Graphics context,
to let Core Text choose the right optical features of the target
point size, as described in WWDC 2015 session 804:
https://developer.apple.com/videos/play/wwdc2015/804/
2018-12-17 00:48:35 +01:00
|
|
|
if (size_table[size_index].to_float () >= ptem)
|
2019-08-24 15:27:14 +02:00
|
|
|
break;
|
2018-02-24 10:19:42 +01:00
|
|
|
|
2019-08-20 12:16:48 +02:00
|
|
|
return roundf (interpolate_at (size_index ? size_index - 1 : 0, ptem,
|
|
|
|
*trackTableEntry, base));
|
2018-10-11 16:18:46 +02:00
|
|
|
}
|
|
|
|
|
2018-12-16 20:08:10 +01:00
|
|
|
bool sanitize (hb_sanitize_context_t *c, const void *base) const
|
2018-10-11 16:18:46 +02:00
|
|
|
{
|
|
|
|
TRACE_SANITIZE (this);
|
2018-11-08 18:18:54 +01:00
|
|
|
return_trace (likely (c->check_struct (this) &&
|
|
|
|
sizeTable.sanitize (c, base, nSizes) &&
|
|
|
|
trackTable.sanitize (c, nTracks, base, nSizes)));
|
2018-02-24 10:19:42 +01:00
|
|
|
}
|
|
|
|
|
2018-02-19 20:57:08 +01:00
|
|
|
protected:
|
2018-04-11 15:11:24 +02:00
|
|
|
HBUINT16 nTracks; /* Number of separate tracks included in this table. */
|
|
|
|
HBUINT16 nSizes; /* Number of point sizes included in this table. */
|
2020-03-10 11:27:46 +01:00
|
|
|
LNNOffsetTo<UnsizedArrayOf<HBFixed>>
|
2018-10-11 16:18:46 +02:00
|
|
|
sizeTable; /* Offset from start of the tracking table to
|
|
|
|
* Array[nSizes] of size values.. */
|
2018-02-26 09:32:11 +01:00
|
|
|
UnsizedArrayOf<TrackTableEntry>
|
2018-04-11 15:11:24 +02:00
|
|
|
trackTable; /* Array[nTracks] of TrackTableEntry records. */
|
2018-02-19 20:57:08 +01:00
|
|
|
|
|
|
|
public:
|
2018-02-21 00:02:25 +01:00
|
|
|
DEFINE_SIZE_ARRAY (8, trackTable);
|
2018-02-19 20:57:08 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct trak
|
|
|
|
{
|
2019-01-22 12:08:57 +01:00
|
|
|
static constexpr hb_tag_t tableTag = HB_AAT_TAG_trak;
|
2018-02-19 20:57:08 +01:00
|
|
|
|
2018-12-17 19:01:01 +01:00
|
|
|
bool has_data () const { return version.to_int (); }
|
2018-10-11 17:10:06 +02:00
|
|
|
|
2018-12-16 20:08:10 +01:00
|
|
|
bool apply (hb_aat_apply_context_t *c) const
|
2018-02-24 10:19:42 +01:00
|
|
|
{
|
|
|
|
TRACE_APPLY (this);
|
2018-02-26 03:54:52 +01:00
|
|
|
|
2018-10-23 12:10:56 +02:00
|
|
|
hb_mask_t trak_mask = c->plan->trak_mask;
|
|
|
|
|
2018-02-24 10:19:42 +01:00
|
|
|
const float ptem = c->font->ptem;
|
2018-04-18 09:39:37 +02:00
|
|
|
if (unlikely (ptem <= 0.f))
|
2018-02-26 03:54:52 +01:00
|
|
|
return_trace (false);
|
|
|
|
|
|
|
|
hb_buffer_t *buffer = c->buffer;
|
|
|
|
if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
|
2018-02-24 10:19:42 +01:00
|
|
|
{
|
2018-02-26 03:54:52 +01:00
|
|
|
const TrackData &trackData = this+horizData;
|
2018-10-18 02:55:47 +02:00
|
|
|
int tracking = trackData.get_tracking (this, ptem);
|
2018-10-11 16:22:01 +02:00
|
|
|
hb_position_t offset_to_add = c->font->em_scalef_x (tracking / 2);
|
|
|
|
hb_position_t advance_to_add = c->font->em_scalef_x (tracking);
|
2018-02-26 03:54:52 +01:00
|
|
|
foreach_grapheme (buffer, start, end)
|
2018-02-24 10:19:42 +01:00
|
|
|
{
|
2019-08-24 15:27:14 +02:00
|
|
|
if (!(buffer->info[start].mask & trak_mask)) continue;
|
2018-02-26 03:54:52 +01:00
|
|
|
buffer->pos[start].x_advance += advance_to_add;
|
2018-10-11 16:22:01 +02:00
|
|
|
buffer->pos[start].x_offset += offset_to_add;
|
2018-02-24 10:19:42 +01:00
|
|
|
}
|
2018-02-26 03:54:52 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const TrackData &trackData = this+vertData;
|
2018-10-18 02:55:47 +02:00
|
|
|
int tracking = trackData.get_tracking (this, ptem);
|
2018-10-11 16:22:01 +02:00
|
|
|
hb_position_t offset_to_add = c->font->em_scalef_y (tracking / 2);
|
|
|
|
hb_position_t advance_to_add = c->font->em_scalef_y (tracking);
|
2018-02-26 03:54:52 +01:00
|
|
|
foreach_grapheme (buffer, start, end)
|
2018-02-24 10:19:42 +01:00
|
|
|
{
|
2019-08-24 15:27:14 +02:00
|
|
|
if (!(buffer->info[start].mask & trak_mask)) continue;
|
2018-02-26 03:54:52 +01:00
|
|
|
buffer->pos[start].y_advance += advance_to_add;
|
2018-10-11 16:22:01 +02:00
|
|
|
buffer->pos[start].y_offset += offset_to_add;
|
2018-02-24 10:19:42 +01:00
|
|
|
}
|
|
|
|
}
|
2018-02-26 03:54:52 +01:00
|
|
|
|
|
|
|
return_trace (true);
|
2018-02-24 10:19:42 +01:00
|
|
|
}
|
|
|
|
|
2018-12-16 20:08:10 +01:00
|
|
|
bool sanitize (hb_sanitize_context_t *c) const
|
2018-11-10 15:38:11 +01:00
|
|
|
{
|
|
|
|
TRACE_SANITIZE (this);
|
|
|
|
|
|
|
|
return_trace (likely (c->check_struct (this) &&
|
|
|
|
version.major == 1 &&
|
|
|
|
horizData.sanitize (c, this, this) &&
|
|
|
|
vertData.sanitize (c, this, this)));
|
|
|
|
}
|
|
|
|
|
2018-02-19 20:57:08 +01:00
|
|
|
protected:
|
2018-11-10 15:38:11 +01:00
|
|
|
FixedVersion<>version; /* Version of the tracking table
|
2019-12-31 13:23:02 +01:00
|
|
|
* (0x00010000u for version 1.0). */
|
|
|
|
HBUINT16 format; /* Format of the tracking table (set to 0). */
|
2018-11-10 15:38:11 +01:00
|
|
|
OffsetTo<TrackData>
|
|
|
|
horizData; /* Offset from start of tracking table to TrackData
|
|
|
|
* for horizontal text (or 0 if none). */
|
|
|
|
OffsetTo<TrackData>
|
|
|
|
vertData; /* Offset from start of tracking table to TrackData
|
|
|
|
* for vertical text (or 0 if none). */
|
|
|
|
HBUINT16 reserved; /* Reserved. Set to 0. */
|
2018-02-19 20:57:08 +01:00
|
|
|
|
|
|
|
public:
|
2018-10-11 16:18:46 +02:00
|
|
|
DEFINE_SIZE_STATIC (12);
|
2018-02-19 20:57:08 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
} /* namespace AAT */
|
|
|
|
|
|
|
|
|
|
|
|
#endif /* HB_AAT_LAYOUT_TRAK_TABLE_HH */
|