1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
23 * Author: Alexander Gelfenbain
38 #include <impfontcharmap.hxx>
39 #ifdef SYSTEM_LIBFIXMATH
40 #include <libfixmath/fix16.hpp>
42 #include <tools/fix16.hxx>
47 #include <rtl/ustring.hxx>
48 #include <rtl/ustrbuf.hxx>
49 #include <sal/log.hxx>
50 #include <o3tl/safeint.hxx>
51 #include <osl/endian.h>
52 #include <osl/thread.h>
53 #include <unotools/tempfile.hxx>
54 #include <fontsubset.hxx>
63 /*- In horizontal writing mode right sidebearing is calculated using this formula
64 *- rsb = aw - (lsb + xMax - xMin) -*/
65 struct TTGlyphMetrics
{
70 sal_uInt16 aw
; /*- Advance Width (horizontal writing mode) */
71 sal_Int16 lsb
; /*- Left sidebearing (horizontal writing mode) */
72 sal_uInt16 ah
; /*- advance height (vertical writing mode) */
77 /*- Data access methods for data stored in big-endian format */
78 static sal_Int16
GetInt16(const sal_uInt8
*ptr
, size_t offset
)
81 assert(ptr
!= nullptr);
83 t
= (ptr
+offset
)[0] << 8 | (ptr
+offset
)[1];
88 static sal_uInt16
GetUInt16(const sal_uInt8
*ptr
, size_t offset
)
91 assert(ptr
!= nullptr);
93 t
= (ptr
+offset
)[0] << 8 | (ptr
+offset
)[1];
98 static sal_Int32
GetInt32(const sal_uInt8
*ptr
, size_t offset
)
101 assert(ptr
!= nullptr);
103 t
= (ptr
+offset
)[0] << 24 | (ptr
+offset
)[1] << 16 |
104 (ptr
+offset
)[2] << 8 | (ptr
+offset
)[3];
109 static sal_uInt32
GetUInt32(const sal_uInt8
*ptr
, size_t offset
)
112 assert(ptr
!= nullptr);
114 t
= (ptr
+offset
)[0] << 24 | (ptr
+offset
)[1] << 16 |
115 (ptr
+offset
)[2] << 8 | (ptr
+offset
)[3];
120 static F16Dot16
fixedMul(F16Dot16 a
, F16Dot16 b
)
122 return fix16_mul(a
, b
);
125 static F16Dot16
fixedDiv(F16Dot16 a
, F16Dot16 b
)
127 return fix16_div(a
, b
);
130 /*- returns a * b / c -*/
131 /* XXX provide a real implementation that preserves accuracy */
132 static F16Dot16
fixedMulDiv(F16Dot16 a
, F16Dot16 b
, F16Dot16 c
)
134 F16Dot16 res
= fixedMul(a
, b
);
135 return fixedDiv(res
, c
);
138 /*- Translate units from TT to PS (standard 1/1000) -*/
139 static int XUnits(int unitsPerEm
, int n
)
141 return (n
* 1000) / unitsPerEm
;
144 /* Outline Extraction functions */
146 /* fills the aw and lsb entries of the TTGlyphMetrics structure from hmtx table -*/
147 static void GetMetrics(AbstractTrueTypeFont
const *ttf
, sal_uInt32 glyphID
, TTGlyphMetrics
*metrics
)
150 const sal_uInt8
* table
= ttf
->table(O_hmtx
, nSize
);
152 metrics
->aw
= metrics
->lsb
= metrics
->ah
= 0;
153 if (!table
|| !ttf
->horzMetricCount())
156 if (glyphID
< ttf
->horzMetricCount())
158 metrics
->aw
= GetUInt16(table
, 4 * glyphID
);
159 metrics
->lsb
= GetInt16(table
, 4 * glyphID
+ 2);
163 metrics
->aw
= GetUInt16(table
, 4 * (ttf
->horzMetricCount() - 1));
164 metrics
->lsb
= GetInt16(table
+ ttf
->horzMetricCount() * 4, (glyphID
- ttf
->horzMetricCount()) * 2);
167 table
= ttf
->table(O_vmtx
, nSize
);
168 if (!table
|| !ttf
->vertMetricCount())
171 if (glyphID
< ttf
->vertMetricCount())
172 metrics
->ah
= GetUInt16(table
, 4 * glyphID
);
174 metrics
->ah
= GetUInt16(table
, 4 * (ttf
->vertMetricCount() - 1));
177 static int GetTTGlyphOutline(AbstractTrueTypeFont
*, sal_uInt32
, std::vector
<ControlPoint
>&, TTGlyphMetrics
*, std::vector
< sal_uInt32
>* );
179 /* returns the number of control points, allocates the pointArray */
180 static int GetSimpleTTOutline(AbstractTrueTypeFont
const *ttf
, sal_uInt32 glyphID
,
181 std::vector
<ControlPoint
>& pointArray
, TTGlyphMetrics
*metrics
)
183 sal_uInt32 nTableSize
;
184 const sal_uInt8
* table
= ttf
->table(O_glyf
, nTableSize
);
190 if (glyphID
>= ttf
->glyphCount())
193 sal_uInt32 nGlyphOffset
= ttf
->glyphOffset(glyphID
);
194 if (nGlyphOffset
> nTableSize
)
197 const sal_uInt8
* ptr
= table
+ nGlyphOffset
;
198 const sal_uInt32 nMaxGlyphSize
= nTableSize
- nGlyphOffset
;
199 constexpr sal_uInt32 nContourOffset
= 10;
200 if (nMaxGlyphSize
< nContourOffset
)
203 const sal_Int16 numberOfContours
= GetInt16(ptr
, GLYF_numberOfContours_offset
);
204 if( numberOfContours
<= 0 ) /*- glyph is not simple */
207 const sal_Int32 nMaxContours
= (nMaxGlyphSize
- nContourOffset
)/2;
208 if (numberOfContours
> nMaxContours
)
211 if (metrics
) { /*- GetCompoundTTOutline() calls this function with NULL metrics -*/
212 metrics
->xMin
= GetInt16(ptr
, GLYF_xMin_offset
);
213 metrics
->yMin
= GetInt16(ptr
, GLYF_yMin_offset
);
214 metrics
->xMax
= GetInt16(ptr
, GLYF_xMax_offset
);
215 metrics
->yMax
= GetInt16(ptr
, GLYF_yMax_offset
);
216 GetMetrics(ttf
, glyphID
, metrics
);
219 /* determine the last point and be extra safe about it. But probably this code is not needed */
220 sal_uInt16 lastPoint
=0;
221 for (i
=0; i
<numberOfContours
; i
++)
223 const sal_uInt16 t
= GetUInt16(ptr
, nContourOffset
+ i
* 2);
228 sal_uInt32 nInstLenOffset
= nContourOffset
+ numberOfContours
* 2;
229 if (nInstLenOffset
+ 2 > nMaxGlyphSize
)
231 sal_uInt16 instLen
= GetUInt16(ptr
, nInstLenOffset
);
233 sal_uInt32 nOffset
= nContourOffset
+ 2 * numberOfContours
+ 2 + instLen
;
234 if (nOffset
> nMaxGlyphSize
)
236 const sal_uInt8
* p
= ptr
+ nOffset
;
238 sal_uInt32 nBytesRemaining
= nMaxGlyphSize
- nOffset
;
239 const sal_uInt32 palen
= lastPoint
+1;
241 //at a minimum its one byte per entry
242 if (palen
> nBytesRemaining
|| lastPoint
> nBytesRemaining
-1)
244 SAL_WARN("vcl.fonts", "Font " << OUString::createFromAscii(ttf
->fileName()) <<
245 "claimed a palen of "
246 << palen
<< " but max bytes remaining is " << nBytesRemaining
);
250 std::vector
<ControlPoint
> pa(palen
);
253 while (i
<= lastPoint
) {
254 if (!nBytesRemaining
)
256 SAL_WARN("vcl.fonts", "short read");
259 sal_uInt8 flag
= *p
++;
261 pa
[i
++].flags
= static_cast<sal_uInt32
>(flag
);
262 if (flag
& 8) { /*- repeat flag */
263 if (!nBytesRemaining
)
265 SAL_WARN("vcl.fonts", "short read");
270 // coverity[tainted_data : FALSE] - i > lastPoint extra checks the n loop bound
271 for (j
=0; j
<n
; j
++) {
272 if (i
> lastPoint
) { /*- if the font is really broken */
275 pa
[i
++].flags
= flag
;
280 /*- Process the X coordinate */
282 for (i
= 0; i
<= lastPoint
; i
++) {
283 if (pa
[i
].flags
& 0x02) {
284 if (!nBytesRemaining
)
286 SAL_WARN("vcl.fonts", "short read");
289 if (pa
[i
].flags
& 0x10) {
290 z
+= static_cast<int>(*p
++);
292 z
-= static_cast<int>(*p
++);
295 } else if ( !(pa
[i
].flags
& 0x10)) {
296 if (nBytesRemaining
< 2)
298 SAL_WARN("vcl.fonts", "short read");
303 nBytesRemaining
-= 2;
305 pa
[i
].x
= static_cast<sal_Int16
>(z
);
308 /*- Process the Y coordinate */
310 for (i
= 0; i
<= lastPoint
; i
++) {
311 if (pa
[i
].flags
& 0x04) {
312 if (!nBytesRemaining
)
314 SAL_WARN("vcl.fonts", "short read");
317 if (pa
[i
].flags
& 0x20) {
323 } else if ( !(pa
[i
].flags
& 0x20)) {
324 if (nBytesRemaining
< 2)
326 SAL_WARN("vcl.fonts", "short read");
331 nBytesRemaining
-= 2;
333 pa
[i
].y
= static_cast<sal_Int16
>(z
);
336 for (i
=0; i
<numberOfContours
; i
++) {
337 sal_uInt16 offset
= GetUInt16(ptr
, 10 + i
* 2);
338 SAL_WARN_IF(offset
>= palen
, "vcl.fonts", "Font " << OUString::createFromAscii(ttf
->fileName()) <<
339 " contour " << i
<< " claimed an illegal offset of "
340 << offset
<< " but max offset is " << palen
-1);
343 pa
[offset
].flags
|= 0x00008000; /*- set the end contour flag */
346 pointArray
= std::move(pa
);
347 return lastPoint
+ 1;
350 static F16Dot16
fromF2Dot14(sal_Int16 n
)
352 // Avoid undefined shift of negative values prior to C++2a:
353 return sal_uInt32(n
) << 2;
356 static int GetCompoundTTOutline(AbstractTrueTypeFont
*ttf
, sal_uInt32 glyphID
, std::vector
<ControlPoint
>& pointArray
,
357 TTGlyphMetrics
*metrics
, std::vector
<sal_uInt32
>& glyphlist
)
359 sal_uInt16 flags
, index
;
361 sal_uInt32 nTableSize
;
362 const sal_uInt8
* table
= ttf
->table(O_glyf
, nTableSize
);
363 std::vector
<ControlPoint
> myPoints
;
364 std::vector
<ControlPoint
> nextComponent
;
366 F16Dot16 a
= 0x10000, b
= 0, c
= 0, d
= 0x10000, m
, n
, abs1
, abs2
, abs3
;
370 if (glyphID
>= ttf
->glyphCount())
373 sal_uInt32 nGlyphOffset
= ttf
->glyphOffset(glyphID
);
374 if (nGlyphOffset
> nTableSize
)
377 const sal_uInt8
* ptr
= table
+ nGlyphOffset
;
378 sal_uInt32 nAvailableBytes
= nTableSize
- nGlyphOffset
;
380 if (GLYF_numberOfContours_offset
+ 2 > nAvailableBytes
)
383 if (GetInt16(ptr
, GLYF_numberOfContours_offset
) != -1) /* number of contours - glyph is not compound */
387 metrics
->xMin
= GetInt16(ptr
, GLYF_xMin_offset
);
388 metrics
->yMin
= GetInt16(ptr
, GLYF_yMin_offset
);
389 metrics
->xMax
= GetInt16(ptr
, GLYF_xMax_offset
);
390 metrics
->yMax
= GetInt16(ptr
, GLYF_yMax_offset
);
391 GetMetrics(ttf
, glyphID
, metrics
);
394 if (nAvailableBytes
< 10)
396 SAL_WARN("vcl.fonts", "short read");
401 nAvailableBytes
-= 10;
405 if (nAvailableBytes
< 4)
407 SAL_WARN("vcl.fonts", "short read");
410 flags
= GetUInt16(ptr
, 0);
411 /* printf("flags: 0x%X\n", flags); */
412 index
= GetUInt16(ptr
, 2);
414 nAvailableBytes
-= 4;
416 if( std::find( glyphlist
.begin(), glyphlist
.end(), index
) != glyphlist
.end() )
418 SAL_WARN("vcl.fonts", "Endless loop found in a compound glyph.");
420 #if OSL_DEBUG_LEVEL > 1
421 std::ostringstream oss
;
422 oss
<< index
<< " -> [";
423 for( const auto& rGlyph
: glyphlist
)
425 oss
<< (int) rGlyph
<< " ";
428 SAL_INFO("vcl.fonts", oss
.str());
434 glyphlist
.push_back( index
);
436 np
= GetTTGlyphOutline(ttf
, index
, nextComponent
, nullptr, &glyphlist
);
438 if( ! glyphlist
.empty() )
439 glyphlist
.pop_back();
443 /* XXX that probably indicates a corrupted font */
444 SAL_WARN("vcl.fonts", "An empty compound!");
445 /* assert(!"An empty compound"); */
449 if ((flags
& USE_MY_METRICS
) && metrics
)
450 GetMetrics(ttf
, index
, metrics
);
452 if (flags
& ARG_1_AND_2_ARE_WORDS
) {
453 if (nAvailableBytes
< 4)
455 SAL_WARN("vcl.fonts", "short read");
458 e
= GetInt16(ptr
, 0);
459 f
= GetInt16(ptr
, 2);
460 /* printf("ARG_1_AND_2_ARE_WORDS: %d %d\n", e & 0xFFFF, f & 0xFFFF); */
462 nAvailableBytes
-= 4;
464 if (nAvailableBytes
< 2)
466 SAL_WARN("vcl.fonts", "short read");
469 if (flags
& ARGS_ARE_XY_VALUES
) { /* args are signed */
470 e
= static_cast<sal_Int8
>(*ptr
++);
471 f
= static_cast<sal_Int8
>(*ptr
++);
472 /* printf("ARGS_ARE_XY_VALUES: %d %d\n", e & 0xFF, f & 0xFF); */
473 } else { /* args are unsigned */
474 /* printf("!ARGS_ARE_XY_VALUES\n"); */
478 nAvailableBytes
-= 2;
484 if (flags
& WE_HAVE_A_SCALE
) {
485 if (nAvailableBytes
< 2)
487 SAL_WARN("vcl.fonts", "short read");
490 a
= fromF2Dot14(GetInt16(ptr
, 0));
493 nAvailableBytes
-= 2;
494 } else if (flags
& WE_HAVE_AN_X_AND_Y_SCALE
) {
495 if (nAvailableBytes
< 4)
497 SAL_WARN("vcl.fonts", "short read");
500 a
= fromF2Dot14(GetInt16(ptr
, 0));
501 d
= fromF2Dot14(GetInt16(ptr
, 2));
503 nAvailableBytes
-= 4;
504 } else if (flags
& WE_HAVE_A_TWO_BY_TWO
) {
505 if (nAvailableBytes
< 8)
507 SAL_WARN("vcl.fonts", "short read");
510 a
= fromF2Dot14(GetInt16(ptr
, 0));
511 b
= fromF2Dot14(GetInt16(ptr
, 2));
512 c
= fromF2Dot14(GetInt16(ptr
, 4));
513 d
= fromF2Dot14(GetInt16(ptr
, 6));
515 nAvailableBytes
-= 8;
518 abs1
= (a
< 0) ? -a
: a
;
519 abs2
= (b
< 0) ? -b
: b
;
520 m
= std::max(abs1
, abs2
);
522 if (abs3
< 0) abs3
= -abs3
;
523 if (abs3
<= 33) m
*= 2;
525 abs1
= (c
< 0) ? -c
: c
;
526 abs2
= (d
< 0) ? -d
: d
;
527 n
= std::max(abs1
, abs2
);
529 if (abs3
< 0) abs3
= -abs3
;
530 if (abs3
<= 33) n
*= 2;
532 SAL_WARN_IF(np
&& (!m
|| !n
), "vcl.fonts", "Parsing error in " << OUString::createFromAscii(ttf
->fileName()) <<
535 if (m
!= 0 && n
!= 0) {
536 for (i
=0; i
<np
; i
++) {
539 cp
.flags
= nextComponent
[i
].flags
;
540 const sal_uInt16 x
= nextComponent
[i
].x
;
541 const sal_uInt16 y
= nextComponent
[i
].y
;
542 t
= o3tl::saturating_add(o3tl::saturating_add(fixedMulDiv(a
, x
<< 16, m
), fixedMulDiv(c
, y
<< 16, m
)), sal_Int32(sal_uInt16(e
) << 16));
543 cp
.x
= static_cast<sal_Int16
>(fixedMul(t
, m
) >> 16);
544 t
= o3tl::saturating_add(o3tl::saturating_add(fixedMulDiv(b
, x
<< 16, n
), fixedMulDiv(d
, y
<< 16, n
)), sal_Int32(sal_uInt16(f
) << 16));
545 cp
.y
= static_cast<sal_Int16
>(fixedMul(t
, n
) >> 16);
547 myPoints
.push_back( cp
);
551 if (myPoints
.size() > SAL_MAX_UINT16
) {
552 SAL_WARN("vcl.fonts", "number of points has to be limited to max value GlyphData::npoints can contain, abandon effort");
557 } while (flags
& MORE_COMPONENTS
);
559 // #i123417# some fonts like IFAOGrec have no outline points in some compound glyphs
560 // so this unlikely but possible scenario should be handled gracefully
561 if( myPoints
.empty() )
564 np
= myPoints
.size();
566 pointArray
= std::move(myPoints
);
571 /* NOTE: GetTTGlyphOutline() returns -1 if the glyphID is incorrect,
572 * but Get{Simple|Compound}GlyphOutline returns 0 in such a case.
574 * NOTE: glyphlist is the stack of glyphs traversed while constructing
575 * a composite glyph. This is a safeguard against endless recursion
576 * in corrupted fonts.
578 static int GetTTGlyphOutline(AbstractTrueTypeFont
*ttf
, sal_uInt32 glyphID
, std::vector
<ControlPoint
>& pointArray
, TTGlyphMetrics
*metrics
, std::vector
< sal_uInt32
>* glyphlist
)
580 sal_uInt32 glyflength
;
581 const sal_uInt8
*table
= ttf
->table(O_glyf
, glyflength
);
582 sal_Int16 numberOfContours
;
587 memset(metrics
, 0, sizeof(TTGlyphMetrics
));
589 if (glyphID
>= ttf
->glyphCount())
592 sal_uInt32 nNextOffset
= ttf
->glyphOffset(glyphID
+ 1);
593 if (nNextOffset
> glyflength
)
596 sal_uInt32 nOffset
= ttf
->glyphOffset(glyphID
);
597 if (nOffset
> nNextOffset
)
600 int length
= nNextOffset
- nOffset
;
601 if (length
== 0) { /*- empty glyphs still have hmtx and vmtx metrics values */
602 if (metrics
) GetMetrics(ttf
, glyphID
, metrics
);
606 const sal_uInt8
* ptr
= table
+ nOffset
;
607 const sal_uInt32 nMaxGlyphSize
= glyflength
- nOffset
;
609 if (nMaxGlyphSize
< 2)
612 numberOfContours
= GetInt16(ptr
, 0);
614 if (numberOfContours
>= 0)
616 res
= GetSimpleTTOutline(ttf
, glyphID
, pointArray
, metrics
);
620 std::vector
< sal_uInt32
> aPrivList
{ glyphID
};
621 res
= GetCompoundTTOutline(ttf
, glyphID
, pointArray
, metrics
, glyphlist
? *glyphlist
: aPrivList
);
627 /*- Extracts a string from the name table and allocates memory for it -*/
629 static OString
nameExtract( const sal_uInt8
* name
, int nTableSize
, int n
, int dbFlag
, OUString
* ucs2result
)
632 const sal_uInt8
* ptr
= name
+ GetUInt16(name
, 4) + GetUInt16(name
+ 6, 12 * n
+ 10);
633 int len
= GetUInt16(name
+6, 12 * n
+ 8);
636 const sal_uInt8
* end_table
= name
+nTableSize
;
637 const int available_space
= ptr
> end_table
? 0 : (end_table
- ptr
);
638 if( (len
<= 0) || len
> available_space
)
648 res
.setLength(len
/2);
649 for (int i
= 0; i
< len
/2; i
++)
651 res
[i
] = *(ptr
+ i
* 2 + 1);
652 SAL_WARN_IF(res
[i
] == 0, "vcl.fonts", "font name is bogus");
656 OUStringBuffer
buf(len
/2);
657 buf
.setLength(len
/2);
658 for (int i
= 0; i
< len
/2; i
++ )
660 buf
[i
] = GetUInt16( ptr
, 2*i
);
661 SAL_WARN_IF(buf
[i
] == 0, "vcl.fonts", "font name is bogus");
663 *ucs2result
= buf
.makeStringAndClear();
667 memcpy(static_cast<void*>(const_cast<char*>(res
.getStr())), ptr
, len
);
670 return res
.makeStringAndClear();
673 static int findname( const sal_uInt8
*name
, sal_uInt16 n
, sal_uInt16 platformID
,
674 sal_uInt16 encodingID
, sal_uInt16 languageID
, sal_uInt16 nameID
)
676 if (n
== 0) return -1;
682 m1
= (platformID
<< 16) | encodingID
;
683 m2
= (languageID
<< 16) | nameID
;
686 const int i
= (l
+ r
) >> 1;
687 t1
= GetUInt32(name
+ 6, i
* 12 + 0);
688 t2
= GetUInt32(name
+ 6, i
* 12 + 4);
690 if (! ((m1
< t1
) || ((m1
== t1
) && (m2
< t2
)))) l
= i
+ 1;
691 if (! ((m1
> t1
) || ((m1
== t1
) && (m2
> t2
)))) r
= i
- 1;
701 /* XXX marlett.ttf uses (3, 0, 1033) instead of (3, 1, 1033) and does not have any Apple tables.
702 * Fix: if (3, 1, 1033) is not found - need to check for (3, 0, 1033)
704 * /d/fonts/ttzh_tw/Big5/Hanyi/ma6b5p uses (1, 0, 19) for English strings, instead of (1, 0, 0)
705 * and does not have (3, 1, 1033)
706 * Fix: if (1, 0, 0) and (3, 1, 1033) are not found need to look for (1, 0, *) - that will
707 * require a change in algorithm
709 * /d/fonts/fdltest/Korean/h2drrm has unsorted names and an unknown (to me) Mac LanguageID,
710 * but (1, 0, 1042) strings usable
711 * Fix: change algorithm, and use (1, 0, *) if both standard Mac and MS strings are not found
714 static void GetNames(AbstractTrueTypeFont
*t
)
716 sal_uInt32 nTableSize
;
717 const sal_uInt8
* table
= t
->table(O_name
, nTableSize
);
721 #if OSL_DEBUG_LEVEL > 1
722 SAL_WARN("vcl.fonts", "O_name table too small.");
727 sal_uInt16 n
= GetUInt16(table
, 2);
729 /* simple sanity check for name table entry count */
730 const size_t nMinRecordSize
= 12;
731 const size_t nSpaceAvailable
= nTableSize
- 6;
732 const size_t nMaxRecords
= nSpaceAvailable
/nMinRecordSize
;
733 if (n
>= nMaxRecords
)
737 bool bPSNameOK
= true;
739 /* PostScript name: preferred Microsoft */
741 if ((r
= findname(table
, n
, 3, 1, 0x0409, 6)) != -1)
742 t
->psname
= nameExtract(table
, nTableSize
, r
, 1, nullptr);
743 if ( t
->psname
.isEmpty() && (r
= findname(table
, n
, 1, 0, 0, 6)) != -1)
744 t
->psname
= nameExtract(table
, nTableSize
, r
, 0, nullptr);
745 if ( t
->psname
.isEmpty() && (r
= findname(table
, n
, 3, 0, 0x0409, 6)) != -1)
747 // some symbol fonts like Marlett have a 3,0 name!
748 t
->psname
= nameExtract(table
, nTableSize
, r
, 1, nullptr);
750 // for embedded font in Ghostscript PDFs
751 if ( t
->psname
.isEmpty() && (r
= findname(table
, n
, 2, 2, 0, 6)) != -1)
753 t
->psname
= nameExtract(table
, nTableSize
, r
, 0, nullptr);
755 if ( t
->psname
.isEmpty() )
757 if (!t
->fileName().empty())
759 const char* pReverse
= t
->fileName().data() + t
->fileName().length();
760 /* take only last token of filename */
761 while (pReverse
!= t
->fileName().data() && *pReverse
!= '/') pReverse
--;
762 if(*pReverse
== '/') pReverse
++;
763 int nReverseLen
= strlen(pReverse
);
764 for (i
=nReverseLen
- 1; i
> 0; i
--)
766 /*- Remove the suffix -*/
767 if (*(pReverse
+ i
) == '.' ) {
772 t
->psname
= OString(std::string_view(pReverse
, nReverseLen
));
775 t
->psname
= "Unknown"_ostr
;
778 /* Font family and subfamily names: preferred Apple */
780 if ((r
= findname(table
, n
, 0, 0, 0, 1)) != -1)
781 t
->family
= nameExtract(table
, nTableSize
, r
, 1, &t
->ufamily
);
782 if ( t
->family
.isEmpty() && (r
= findname(table
, n
, 3, 1, 0x0409, 1)) != -1)
783 t
->family
= nameExtract(table
, nTableSize
, r
, 1, &t
->ufamily
);
784 if ( t
->family
.isEmpty() && (r
= findname(table
, n
, 1, 0, 0, 1)) != -1)
785 t
->family
= nameExtract(table
, nTableSize
, r
, 0, nullptr);
786 if ( t
->family
.isEmpty() && (r
= findname(table
, n
, 3, 1, 0x0411, 1)) != -1)
787 t
->family
= nameExtract(table
, nTableSize
, r
, 1, &t
->ufamily
);
788 if ( t
->family
.isEmpty() && (r
= findname(table
, n
, 3, 0, 0x0409, 1)) != -1)
789 t
->family
= nameExtract(table
, nTableSize
, r
, 1, &t
->ufamily
);
790 if ( t
->family
.isEmpty() )
791 t
->family
= t
->psname
;
793 t
->subfamily
.clear();
794 t
->usubfamily
.clear();
795 if ((r
= findname(table
, n
, 1, 0, 0, 2)) != -1)
796 t
->subfamily
= nameExtract(table
, nTableSize
, r
, 0, &t
->usubfamily
);
797 if ( t
->subfamily
.isEmpty() && (r
= findname(table
, n
, 3, 1, 0x0409, 2)) != -1)
798 t
->subfamily
= nameExtract(table
, nTableSize
, r
, 1, &t
->usubfamily
);
800 /* #i60349# sanity check psname
801 * psname practically has to be 7bit ASCII and should not contain spaces
802 * there is a class of broken fonts which do not fulfill that at all, so let's try
803 * if the family name is 7bit ASCII and take it instead if so
806 for( i
= 0; i
< t
->psname
.getLength() && bPSNameOK
; i
++ )
807 if( t
->psname
[ i
] < 33 || (t
->psname
[ i
] & 0x80) )
812 /* check if family is a suitable replacement */
813 if( t
->ufamily
.isEmpty() && t
->family
.isEmpty() )
816 bool bReplace
= true;
818 for( i
= 0; i
< t
->ufamily
.getLength() && bReplace
; i
++ )
819 if( t
->ufamily
[ i
] < 33 || t
->ufamily
[ i
] > 127 )
823 t
->psname
= t
->family
;
827 /*- Public functions */
829 int CountTTCFonts(const char* fname
)
835 if (sscanf(fname
, "/:FD:/%d%n", &nFD
, &n
) == 1 && fname
[n
] == '\0')
837 lseek(nFD
, 0, SEEK_SET
);
838 int nDupFd
= dup(nFD
);
839 fd
= nDupFd
!= -1 ? fdopen(nDupFd
, "rb") : nullptr;
843 fd
= fopen(fname
, "rb");
849 sal_uInt8 buffer
[12];
850 if (fread(buffer
, 1, 12, fd
) == 12) {
851 if(GetUInt32(buffer
, 0) == T_ttcf
)
852 nFonts
= GetUInt32(buffer
, 8);
857 fseek(fd
, 0, SEEK_END
);
858 sal_uInt64 fileSize
= ftell(fd
);
860 //Feel free to calc the exact max possible number of fonts a file
861 //could contain given its physical size. But this will clamp it to
862 //a sane starting point
863 //http://processingjs.nihongoresources.com/the_smallest_font/
864 //https://github.com/grzegorzrolek/null-ttf
865 const int nMaxFontsPossible
= fileSize
/ 528;
866 if (nFonts
> nMaxFontsPossible
)
868 SAL_WARN("vcl.fonts", "font file " << fname
<<" claims to have "
869 << nFonts
<< " fonts, but only "
870 << nMaxFontsPossible
<< " are possible");
871 nFonts
= nMaxFontsPossible
;
881 SFErrCodes
OpenTTFontFile(const char* fname
, sal_uInt32 facenum
, TrueTypeFont
** ttf
,
882 const FontCharMapRef xCharMap
)
888 if (!fname
|| !*fname
) return SFErrCodes::BadFile
;
890 *ttf
= new TrueTypeFont(fname
, xCharMap
);
892 return SFErrCodes::Memory
;
894 if( (*ttf
)->fileName().empty() )
896 ret
= SFErrCodes::Memory
;
902 if (sscanf(fname
, "/:FD:/%d%n", &nFD
, &n
) == 1 && fname
[n
] == '\0')
904 lseek(nFD
, 0, SEEK_SET
);
908 fd
= open(fname
, O_RDONLY
);
911 ret
= SFErrCodes::BadFile
;
915 if (fstat(fd
, &st
) == -1) {
916 ret
= SFErrCodes::FileIo
;
920 (*ttf
)->fsize
= st
.st_size
;
922 /* On Mac OS, most likely will happen if a Mac user renames a font file
923 * to be .ttf when it's really a Mac resource-based font.
924 * Size will be 0, but fonts smaller than 4 bytes would be broken anyway.
926 if ((*ttf
)->fsize
== 0) {
927 ret
= SFErrCodes::BadFile
;
931 if (((*ttf
)->ptr
= static_cast<sal_uInt8
*>(mmap(nullptr, (*ttf
)->fsize
, PROT_READ
, MAP_SHARED
, fd
, 0))) == MAP_FAILED
) {
932 ret
= SFErrCodes::Memory
;
936 ret
= (*ttf
)->open(facenum
);
939 if (fd
!= -1) close(fd
);
940 if (ret
!= SFErrCodes::Ok
)
949 SFErrCodes
OpenTTFontBuffer(const void* pBuffer
, sal_uInt32 nLen
, sal_uInt32 facenum
, TrueTypeFont
** ttf
,
950 const FontCharMapRef xCharMap
)
952 *ttf
= new TrueTypeFont(nullptr, xCharMap
);
953 if( *ttf
== nullptr )
954 return SFErrCodes::Memory
;
956 (*ttf
)->fsize
= nLen
;
957 (*ttf
)->ptr
= const_cast<sal_uInt8
*>(static_cast<sal_uInt8
const *>(pBuffer
));
959 SFErrCodes ret
= (*ttf
)->open(facenum
);
960 if (ret
!= SFErrCodes::Ok
)
970 bool withinBounds(sal_uInt32 tdoffset
, sal_uInt32 moreoffset
, sal_uInt32 len
, sal_uInt32 available
)
973 if (o3tl::checked_add(tdoffset
, moreoffset
, result
))
975 if (o3tl::checked_add(result
, len
, result
))
977 return result
<= available
;
981 AbstractTrueTypeFont::AbstractTrueTypeFont(const char* pFileName
, const FontCharMapRef xCharMap
)
982 : m_nGlyphs(0xFFFFFFFF)
986 , m_xCharMap(xCharMap
)
987 , m_bMicrosoftSymbolEncoded(false)
990 m_sFileName
= pFileName
;
993 AbstractTrueTypeFont::~AbstractTrueTypeFont()
997 TrueTypeFont::TrueTypeFont(const char* pFileName
, const FontCharMapRef xCharMap
)
998 : AbstractTrueTypeFont(pFileName
, xCharMap
)
1005 TrueTypeFont::~TrueTypeFont()
1007 #if !defined(_WIN32)
1008 if (!fileName().empty())
1013 void CloseTTFont(TrueTypeFont
* ttf
) { delete ttf
; }
1015 SFErrCodes
AbstractTrueTypeFont::initialize()
1017 SFErrCodes ret
= indexGlyphData();
1018 if (ret
!= SFErrCodes::Ok
)
1023 return SFErrCodes::Ok
;
1026 sal_uInt32
AbstractTrueTypeFont::glyphOffset(sal_uInt32 glyphID
) const
1028 if (m_aGlyphOffsets
.empty()) // the O_CFF and Bitmap cases
1030 return m_aGlyphOffsets
[glyphID
];
1033 SFErrCodes
AbstractTrueTypeFont::indexGlyphData()
1035 if (!(hasTable(O_maxp
) && hasTable(O_head
) && hasTable(O_name
) && hasTable(O_cmap
)))
1036 return SFErrCodes::TtFormat
;
1038 sal_uInt32 table_size
;
1039 const sal_uInt8
* table
= this->table(O_maxp
, table_size
);
1040 m_nGlyphs
= table_size
>= 6 ? GetUInt16(table
, 4) : 0;
1042 table
= this->table(O_head
, table_size
);
1043 if (table_size
< HEAD_Length
)
1044 return SFErrCodes::TtFormat
;
1046 m_nUnitsPerEm
= GetUInt16(table
, HEAD_unitsPerEm_offset
);
1047 int indexfmt
= GetInt16(table
, HEAD_indexToLocFormat_offset
);
1049 if (((indexfmt
!= 0) && (indexfmt
!= 1)) || (m_nUnitsPerEm
<= 0))
1050 return SFErrCodes::TtFormat
;
1052 if (hasTable(O_glyf
) && (table
= this->table(O_loca
, table_size
))) /* TTF or TTF-OpenType */
1054 int k
= (table_size
/ (indexfmt
? 4 : 2)) - 1;
1055 if (k
< static_cast<int>(m_nGlyphs
)) /* Hack for broken Chinese fonts */
1058 m_aGlyphOffsets
.clear();
1059 m_aGlyphOffsets
.reserve(m_nGlyphs
+ 1);
1060 for (int i
= 0; i
<= static_cast<int>(m_nGlyphs
); ++i
)
1061 m_aGlyphOffsets
.push_back(indexfmt
? GetUInt32(table
, i
<< 2) : static_cast<sal_uInt32
>(GetUInt16(table
, i
<< 1)) << 1);
1063 else if (this->table(O_CFF
, table_size
)) /* PS-OpenType */
1065 int k
= (table_size
/ 2) - 1; /* set a limit here, presumably much lower than the table size, but establishes some sort of physical bound */
1066 if (k
< static_cast<int>(m_nGlyphs
))
1069 m_aGlyphOffsets
.clear();
1070 /* TODO: implement to get subsetting */
1073 // Bitmap font, accept for now.
1074 // TODO: We only need this for fonts with CBDT table since they usually
1075 // lack glyf or CFF table, the check should be more specific, or better
1076 // non-subsetting code should not be calling this.
1077 m_aGlyphOffsets
.clear();
1080 table
= this->table(O_hhea
, table_size
);
1081 m_nHorzMetrics
= (table
&& table_size
>= 36) ? GetUInt16(table
, 34) : 0;
1083 table
= this->table(O_vhea
, table_size
);
1084 m_nVertMetrics
= (table
&& table_size
>= 36) ? GetUInt16(table
, 34) : 0;
1086 if (!m_xCharMap
.is())
1088 table
= this->table(O_cmap
, table_size
);
1089 m_bMicrosoftSymbolEncoded
= HasMicrosoftSymbolCmap(table
, table_size
);
1092 m_bMicrosoftSymbolEncoded
= m_xCharMap
->isMicrosoftSymbolMap();
1094 return SFErrCodes::Ok
;
1097 SFErrCodes
TrueTypeFont::open(sal_uInt32 facenum
)
1100 return SFErrCodes::TtFormat
;
1103 sal_uInt32 length
, tag
;
1104 sal_uInt32 tdoffset
= 0; /* offset to TableDirectory in a TTC file. For TTF files is 0 */
1106 sal_uInt32 TTCTag
= GetInt32(ptr
, 0);
1108 if ((TTCTag
== 0x00010000) || (TTCTag
== T_true
)) {
1110 } else if (TTCTag
== T_otto
) { /* PS-OpenType font */
1112 } else if (TTCTag
== T_ttcf
) { /* TrueType collection */
1113 if (!withinBounds(12, 4 * facenum
, sizeof(sal_uInt32
), fsize
))
1114 return SFErrCodes::FontNo
;
1115 sal_uInt32 Version
= GetUInt32(ptr
, 4);
1116 if (Version
!= 0x00010000 && Version
!= 0x00020000) {
1117 return SFErrCodes::TtFormat
;
1119 if (facenum
>= GetUInt32(ptr
, 8))
1120 return SFErrCodes::FontNo
;
1121 tdoffset
= GetUInt32(ptr
, 12 + 4 * facenum
);
1123 return SFErrCodes::TtFormat
;
1126 if (withinBounds(tdoffset
, 0, 4 + sizeof(sal_uInt16
), fsize
))
1127 ntables
= GetUInt16(ptr
+ tdoffset
, 4);
1129 if (ntables
>= 128 || ntables
== 0)
1130 return SFErrCodes::TtFormat
;
1132 /* parse the tables */
1133 for (i
= 0; i
< static_cast<int>(ntables
); i
++)
1136 const sal_uInt32 nStart
= tdoffset
+ 12;
1137 const sal_uInt32 nOffset
= 16 * i
;
1138 if (withinBounds(nStart
, nOffset
, sizeof(sal_uInt32
), fsize
))
1139 tag
= GetUInt32(ptr
+ nStart
, nOffset
);
1141 tag
= static_cast<sal_uInt32
>(-1);
1143 case T_maxp
: nIndex
= O_maxp
; break;
1144 case T_glyf
: nIndex
= O_glyf
; break;
1145 case T_head
: nIndex
= O_head
; break;
1146 case T_loca
: nIndex
= O_loca
; break;
1147 case T_name
: nIndex
= O_name
; break;
1148 case T_hhea
: nIndex
= O_hhea
; break;
1149 case T_hmtx
: nIndex
= O_hmtx
; break;
1150 case T_cmap
: nIndex
= O_cmap
; break;
1151 case T_vhea
: nIndex
= O_vhea
; break;
1152 case T_vmtx
: nIndex
= O_vmtx
; break;
1153 case T_OS2
: nIndex
= O_OS2
; break;
1154 case T_post
: nIndex
= O_post
; break;
1155 case T_cvt
: nIndex
= O_cvt
; break;
1156 case T_prep
: nIndex
= O_prep
; break;
1157 case T_fpgm
: nIndex
= O_fpgm
; break;
1158 case T_CFF
: nIndex
= O_CFF
; break;
1159 default: nIndex
= -1; break;
1162 if ((nIndex
>= 0) && withinBounds(nStart
, nOffset
, 12 + sizeof(sal_uInt32
), fsize
))
1164 sal_uInt32 nTableOffset
= GetUInt32(ptr
+ nStart
, nOffset
+ 8);
1165 length
= GetUInt32(ptr
+ nStart
, nOffset
+ 12);
1166 m_aTableList
[nIndex
].pData
= ptr
+ nTableOffset
;
1167 m_aTableList
[nIndex
].nSize
= length
;
1171 /* Fixup offsets when only a TTC extract was provided */
1172 if (facenum
== sal_uInt32(~0))
1174 sal_uInt8
* pHead
= const_cast<sal_uInt8
*>(m_aTableList
[O_head
].pData
);
1176 return SFErrCodes::TtFormat
;
1178 /* limit Head candidate to TTC extract's limits */
1179 if (pHead
> ptr
+ (fsize
- 54))
1180 pHead
= ptr
+ (fsize
- 54);
1182 /* TODO: find better method than searching head table's magic */
1183 sal_uInt8
* p
= nullptr;
1184 for (p
= pHead
+ 12; p
> ptr
; --p
)
1186 if( p
[0]==0x5F && p
[1]==0x0F && p
[2]==0x3C && p
[3]==0xF5 ) {
1187 int nDelta
= (pHead
+ 12) - p
;
1189 for( int j
= 0; j
< NUM_TAGS
; ++j
)
1191 m_aTableList
[j
].pData
-= nDelta
;
1196 return SFErrCodes::TtFormat
;
1199 /* Check the table offsets after TTC correction */
1200 for (i
=0; i
<NUM_TAGS
; i
++) {
1201 /* sanity check: table must lay completely within the file
1202 * at this point one could check the checksum of all contained
1203 * tables, but this would be quite time intensive.
1204 * Try to fix tables, so we can cope with minor problems.
1207 if (m_aTableList
[i
].pData
< ptr
)
1209 #if OSL_DEBUG_LEVEL > 1
1210 SAL_WARN_IF(m_aTableList
[i
].pData
, "vcl.fonts", "font file " << fileName()
1211 << " has bad table offset "
1212 << (sal_uInt8
*)m_aTableList
[i
].pData
- ptr
1213 << "d (tagnum=" << i
<< ").");
1215 m_aTableList
[i
].nSize
= 0;
1216 m_aTableList
[i
].pData
= nullptr;
1218 else if (const_cast<sal_uInt8
*>(m_aTableList
[i
].pData
) + m_aTableList
[i
].nSize
> ptr
+ fsize
)
1220 sal_PtrDiff nMaxLen
= (ptr
+ fsize
) - m_aTableList
[i
].pData
;
1223 m_aTableList
[i
].nSize
= nMaxLen
;
1224 #if OSL_DEBUG_LEVEL > 1
1225 SAL_WARN("vcl.fonts", "font file " << fileName()
1226 << " has too big table (tagnum=" << i
<< ").");
1231 /* At this point TrueTypeFont is constructed, now need to verify the font format
1232 and read the basic font properties */
1234 return AbstractTrueTypeFont::initialize();
1237 int GetTTGlyphPoints(AbstractTrueTypeFont
*ttf
, sal_uInt32 glyphID
, std::vector
<ControlPoint
>& pointArray
)
1239 return GetTTGlyphOutline(ttf
, glyphID
, pointArray
, nullptr, nullptr);
1242 int GetTTGlyphComponents(AbstractTrueTypeFont
*ttf
, sal_uInt32 glyphID
, std::vector
< sal_uInt32
>& glyphlist
)
1246 if (glyphID
>= ttf
->glyphCount())
1249 sal_uInt32 glyflength
;
1250 const sal_uInt8
* glyf
= ttf
->table(O_glyf
, glyflength
);
1252 sal_uInt32 nNextOffset
= ttf
->glyphOffset(glyphID
+ 1);
1253 if (nNextOffset
> glyflength
)
1256 sal_uInt32 nOffset
= ttf
->glyphOffset(glyphID
);
1257 if (nOffset
> nNextOffset
)
1260 if (std::find(glyphlist
.begin(), glyphlist
.end(), glyphID
) != glyphlist
.end())
1262 SAL_WARN("vcl.fonts", "Endless loop found in a compound glyph.");
1266 glyphlist
.push_back( glyphID
);
1269 if (nOffset
== nNextOffset
)
1272 const auto* ptr
= glyf
+ nOffset
;
1273 sal_uInt32 nRemainingData
= glyflength
- nOffset
;
1275 if (nRemainingData
>= 10 && GetInt16(ptr
, 0) == -1) {
1276 sal_uInt16 flags
, index
;
1278 nRemainingData
-= 10;
1280 if (nRemainingData
< 4)
1282 SAL_WARN("vcl.fonts", "short read");
1285 flags
= GetUInt16(ptr
, 0);
1286 index
= GetUInt16(ptr
, 2);
1289 nRemainingData
-= 4;
1290 n
+= GetTTGlyphComponents(ttf
, index
, glyphlist
);
1292 sal_uInt32 nAdvance
;
1293 if (flags
& ARG_1_AND_2_ARE_WORDS
) {
1299 if (flags
& WE_HAVE_A_SCALE
) {
1301 } else if (flags
& WE_HAVE_AN_X_AND_Y_SCALE
) {
1303 } else if (flags
& WE_HAVE_A_TWO_BY_TWO
) {
1306 if (nRemainingData
< nAdvance
)
1308 SAL_WARN("vcl.fonts", "short read");
1312 nRemainingData
-= nAdvance
;
1313 } while (flags
& MORE_COMPONENTS
);
1319 SFErrCodes
CreateTTFromTTGlyphs(AbstractTrueTypeFont
*ttf
,
1320 std::vector
<sal_uInt8
>& rOutBuffer
,
1321 sal_uInt16
const *glyphArray
,
1322 sal_uInt8
const *encoding
,
1325 std::unique_ptr
<TrueTypeTableGeneric
> cvt
, prep
, fpgm
, os2
;
1326 std::unique_ptr
<TrueTypeTableName
> name
;
1327 std::unique_ptr
<TrueTypeTableMaxp
> maxp
;
1328 std::unique_ptr
<TrueTypeTableHhea
> hhea
;
1329 std::unique_ptr
<TrueTypeTableHead
> head
;
1330 std::unique_ptr
<TrueTypeTableGlyf
> glyf
;
1331 std::unique_ptr
<TrueTypeTableCmap
> cmap
;
1332 std::unique_ptr
<TrueTypeTablePost
> post
;
1336 TrueTypeCreator
ttcr(T_true
);
1340 std::vector
<NameRecord
> names
;
1341 GetTTNameRecords(ttf
, names
);
1342 name
.reset(new TrueTypeTableName(std::move(names
)));
1345 sal_uInt32 nTableSize
;
1346 const sal_uInt8
* p
= ttf
->table(O_maxp
, nTableSize
);
1347 maxp
.reset(new TrueTypeTableMaxp(p
, nTableSize
));
1350 p
= ttf
->table(O_hhea
, nTableSize
);
1351 if (p
&& nTableSize
>= HHEA_caretSlopeRun_offset
+ 2)
1352 hhea
.reset(new TrueTypeTableHhea(GetInt16(p
, HHEA_ascender_offset
), GetInt16(p
, HHEA_descender_offset
), GetInt16(p
, HHEA_lineGap_offset
), GetInt16(p
, HHEA_caretSlopeRise_offset
), GetInt16(p
, HHEA_caretSlopeRun_offset
)));
1354 hhea
.reset(new TrueTypeTableHhea(0, 0, 0, 0, 0));
1358 p
= ttf
->table(O_head
, nTableSize
);
1359 assert(p
!= nullptr);
1360 head
.reset(new TrueTypeTableHead(GetInt32(p
, HEAD_fontRevision_offset
),
1361 GetUInt16(p
, HEAD_flags_offset
),
1362 GetUInt16(p
, HEAD_unitsPerEm_offset
),
1363 p
+HEAD_created_offset
,
1364 GetUInt16(p
, HEAD_macStyle_offset
),
1365 GetUInt16(p
, HEAD_lowestRecPPEM_offset
),
1366 GetInt16(p
, HEAD_fontDirectionHint_offset
)));
1370 glyf
.reset(new TrueTypeTableGlyf());
1371 std::unique_ptr
<sal_uInt32
[]> gID(new sal_uInt32
[nGlyphs
]);
1373 for (i
= 0; i
< nGlyphs
; i
++) {
1374 gID
[i
] = glyf
->glyfAdd(GetTTRawGlyphData(ttf
, glyphArray
[i
]), ttf
);
1378 cmap
.reset(new TrueTypeTableCmap());
1380 for (i
=0; i
< nGlyphs
; i
++) {
1381 cmap
->cmapAdd(0x010000, encoding
[i
], gID
[i
]);
1385 if ((p
= ttf
->table(O_cvt
, nTableSize
)) != nullptr)
1386 cvt
.reset(new TrueTypeTableGeneric(T_cvt
, nTableSize
, p
));
1389 if ((p
= ttf
->table(O_prep
, nTableSize
)) != nullptr)
1390 prep
.reset(new TrueTypeTableGeneric(T_prep
, nTableSize
, p
));
1393 if ((p
= ttf
->table(O_fpgm
, nTableSize
)) != nullptr)
1394 fpgm
.reset(new TrueTypeTableGeneric(T_fpgm
, nTableSize
, p
));
1397 if ((p
= ttf
->table(O_post
, nTableSize
)) != nullptr)
1399 sal_Int32 nItalic
= (POST_italicAngle_offset
+ 4 < nTableSize
) ?
1400 GetInt32(p
, POST_italicAngle_offset
) : 0;
1401 sal_Int16 nPosition
= (POST_underlinePosition_offset
+ 2 < nTableSize
) ?
1402 GetInt16(p
, POST_underlinePosition_offset
) : 0;
1403 sal_Int16 nThickness
= (POST_underlineThickness_offset
+ 2 < nTableSize
) ?
1404 GetInt16(p
, POST_underlineThickness_offset
) : 0;
1405 sal_uInt32 nFixedPitch
= (POST_isFixedPitch_offset
+ 4 < nTableSize
) ?
1406 GetUInt32(p
, POST_isFixedPitch_offset
) : 0;
1408 post
.reset(new TrueTypeTablePost(0x00030000,
1410 nThickness
, nFixedPitch
));
1413 post
.reset(new TrueTypeTablePost(0x00030000, 0, 0, 0, 0));
1415 ttcr
.AddTable(std::move(name
)); ttcr
.AddTable(std::move(maxp
)); ttcr
.AddTable(std::move(hhea
));
1416 ttcr
.AddTable(std::move(head
)); ttcr
.AddTable(std::move(glyf
)); ttcr
.AddTable(std::move(cmap
));
1417 ttcr
.AddTable(std::move(cvt
)); ttcr
.AddTable(std::move(prep
)); ttcr
.AddTable(std::move(fpgm
));
1418 ttcr
.AddTable(std::move(post
)); ttcr
.AddTable(std::move(os2
));
1420 res
= ttcr
.StreamToMemory(rOutBuffer
);
1421 #if OSL_DEBUG_LEVEL > 1
1422 SAL_WARN_IF(res
!= SFErrCodes::Ok
, "vcl.fonts", "StreamToMemory: error code: "
1423 << (int) res
<< ".");
1431 void FillFontSubsetInfo(AbstractTrueTypeFont
* ttf
, FontSubsetInfo
& rInfo
)
1433 TTGlobalFontInfo aTTInfo
;
1434 GetTTGlobalFontInfo(ttf
, &aTTInfo
);
1436 rInfo
.m_aPSName
= OUString::fromUtf8(aTTInfo
.psname
);
1437 rInfo
.m_nFontType
= FontType::SFNT_TTF
;
1439 = tools::Rectangle(Point(aTTInfo
.xMin
, aTTInfo
.yMin
), Point(aTTInfo
.xMax
, aTTInfo
.yMax
));
1440 rInfo
.m_nCapHeight
= aTTInfo
.yMax
; // Well ...
1441 rInfo
.m_nAscent
= aTTInfo
.winAscent
;
1442 rInfo
.m_nDescent
= aTTInfo
.winDescent
;
1444 // mac fonts usually do not have an OS2-table
1445 // => get valid ascent/descent values from other tables
1446 if (!rInfo
.m_nAscent
)
1447 rInfo
.m_nAscent
= +aTTInfo
.typoAscender
;
1448 if (!rInfo
.m_nAscent
)
1449 rInfo
.m_nAscent
= +aTTInfo
.ascender
;
1450 if (!rInfo
.m_nDescent
)
1451 rInfo
.m_nDescent
= +aTTInfo
.typoDescender
;
1452 if (!rInfo
.m_nDescent
)
1453 rInfo
.m_nDescent
= -aTTInfo
.descender
;
1455 rInfo
.m_bFilled
= true;
1458 bool CreateCFFfontSubset(const unsigned char* pFontBytes
, int nByteLength
,
1459 std::vector
<sal_uInt8
>& rOutBuffer
, const sal_GlyphId
* pGlyphIds
,
1460 const sal_uInt8
* pEncoding
, int nGlyphCount
, FontSubsetInfo
& rInfo
)
1462 utl::TempFileFast aTempFile
;
1463 SvStream
* pStream
= aTempFile
.GetStream(StreamMode::READWRITE
);
1465 rInfo
.LoadFont(FontType::CFF_FONT
, pFontBytes
, nByteLength
);
1466 bool bRet
= rInfo
.CreateFontSubset(FontType::TYPE1_PFB
, pStream
, pGlyphIds
, pEncoding
,
1471 rOutBuffer
.resize(pStream
->TellEnd());
1473 auto nRead
= pStream
->ReadBytes(rOutBuffer
.data(), rOutBuffer
.size());
1474 if (nRead
!= rOutBuffer
.size())
1485 bool CreateTTFfontSubset(vcl::AbstractTrueTypeFont
& rTTF
, std::vector
<sal_uInt8
>& rOutBuffer
,
1486 const sal_GlyphId
* pGlyphIds
, const sal_uInt8
* pEncoding
,
1487 const int nOrigGlyphCount
, FontSubsetInfo
& rInfo
)
1489 // Get details about the subset font.
1490 FillFontSubsetInfo(&rTTF
, rInfo
);
1492 // Shortcut for CFF-subsetting.
1494 const sal_uInt8
* pCFF
= rTTF
.table(O_CFF
, nCFF
);
1496 return CreateCFFfontSubset(pCFF
, nCFF
, rOutBuffer
, pGlyphIds
, pEncoding
,
1497 nOrigGlyphCount
, rInfo
);
1499 // Multiple questions:
1500 // - Why is there a glyph limit?
1501 // MacOS used to handle 257 glyphs...
1502 // Also the much more complex PrintFontManager variant has this limit.
1503 // Also the very first implementation has the limit in
1504 // commit 8789ed701e98031f2a1657ea0dfd6f7a0b050992
1505 // - Why doesn't the PrintFontManager care about the fake glyph? It
1506 // is used on all unx platforms to create the subset font.
1507 // - Should the SAL_WARN actually be asserts, like on MacOS?
1508 if (nOrigGlyphCount
> 256)
1510 SAL_WARN("vcl.fonts", "too many glyphs for subsetting");
1514 int nGlyphCount
= nOrigGlyphCount
;
1515 sal_uInt16 aShortIDs
[256];
1516 sal_uInt8 aTempEncs
[256];
1518 // handle the undefined / first font glyph
1519 int nNotDef
= -1, i
;
1520 for (i
= 0; i
< nGlyphCount
; ++i
)
1522 aTempEncs
[i
] = pEncoding
[i
];
1523 aShortIDs
[i
] = static_cast<sal_uInt16
>(pGlyphIds
[i
]);
1529 // nNotDef glyph must be in pos 0 => swap glyphids
1534 if (nGlyphCount
== 256)
1536 SAL_WARN("vcl.fonts", "too many glyphs for subsetting");
1539 nNotDef
= nGlyphCount
++;
1542 aShortIDs
[nNotDef
] = aShortIDs
[0];
1543 aTempEncs
[nNotDef
] = aTempEncs
[0];
1548 // write subset into destination file
1549 return (CreateTTFromTTGlyphs(&rTTF
, rOutBuffer
, aShortIDs
, aTempEncs
, nGlyphCount
)
1550 == vcl::SFErrCodes::Ok
);
1553 bool GetTTGlobalFontHeadInfo(const AbstractTrueTypeFont
*ttf
, int& xMin
, int& yMin
, int& xMax
, int& yMax
, sal_uInt16
& macStyle
)
1555 sal_uInt32 table_size
;
1556 const sal_uInt8
* table
= ttf
->table(O_head
, table_size
);
1557 if (table_size
< 46)
1560 const int UPEm
= ttf
->unitsPerEm();
1563 xMin
= XUnits(UPEm
, GetInt16(table
, HEAD_xMin_offset
));
1564 yMin
= XUnits(UPEm
, GetInt16(table
, HEAD_yMin_offset
));
1565 xMax
= XUnits(UPEm
, GetInt16(table
, HEAD_xMax_offset
));
1566 yMax
= XUnits(UPEm
, GetInt16(table
, HEAD_yMax_offset
));
1567 macStyle
= GetUInt16(table
, HEAD_macStyle_offset
);
1571 void GetTTGlobalFontInfo(AbstractTrueTypeFont
*ttf
, TTGlobalFontInfo
*info
)
1573 int UPEm
= ttf
->unitsPerEm();
1575 info
->family
= ttf
->family
;
1576 info
->ufamily
= ttf
->ufamily
;
1577 info
->subfamily
= ttf
->subfamily
;
1578 info
->usubfamily
= ttf
->usubfamily
;
1579 info
->psname
= ttf
->psname
;
1580 info
->microsoftSymbolEncoded
= ttf
->IsMicrosoftSymbolEncoded();
1582 sal_uInt32 table_size
;
1583 const sal_uInt8
* table
= ttf
->table(O_OS2
, table_size
);
1584 if (table_size
>= 42)
1586 info
->weight
= GetUInt16(table
, OS2_usWeightClass_offset
);
1587 info
->width
= GetUInt16(table
, OS2_usWidthClass_offset
);
1589 if (table_size
>= OS2_V0_length
&& UPEm
!= 0) {
1590 info
->typoAscender
= XUnits(UPEm
,GetInt16(table
, OS2_typoAscender_offset
));
1591 info
->typoDescender
= XUnits(UPEm
, GetInt16(table
, OS2_typoDescender_offset
));
1592 info
->typoLineGap
= XUnits(UPEm
, GetInt16(table
, OS2_typoLineGap_offset
));
1593 info
->winAscent
= XUnits(UPEm
, GetUInt16(table
, OS2_winAscent_offset
));
1594 info
->winDescent
= XUnits(UPEm
, GetUInt16(table
, OS2_winDescent_offset
));
1595 /* sanity check; some fonts treat winDescent as signed
1596 * violating the standard */
1597 if( info
->winDescent
> 5*UPEm
)
1598 info
->winDescent
= XUnits(UPEm
, GetInt16(table
, OS2_winDescent_offset
));
1600 memcpy(info
->panose
, table
+ OS2_panose_offset
, OS2_panoseNbBytes_offset
);
1601 info
->typeFlags
= GetUInt16( table
, OS2_fsType_offset
);
1604 table
= ttf
->table(O_post
, table_size
);
1605 if (table_size
>= 12 + sizeof(sal_uInt32
))
1607 info
->pitch
= GetUInt32(table
, POST_isFixedPitch_offset
);
1608 info
->italicAngle
= GetInt32(table
, POST_italicAngle_offset
);
1611 GetTTGlobalFontHeadInfo(ttf
, info
->xMin
, info
->yMin
, info
->xMax
, info
->yMax
, info
->macStyle
);
1613 table
= ttf
->table(O_hhea
, table_size
);
1614 if (table_size
>= 10 && UPEm
!= 0)
1616 info
->ascender
= XUnits(UPEm
, GetInt16(table
, HHEA_ascender_offset
));
1617 info
->descender
= XUnits(UPEm
, GetInt16(table
, HHEA_descender_offset
));
1618 info
->linegap
= XUnits(UPEm
, GetInt16(table
, HHEA_lineGap_offset
));
1622 std::unique_ptr
<GlyphData
> GetTTRawGlyphData(AbstractTrueTypeFont
*ttf
, sal_uInt32 glyphID
)
1624 if (glyphID
>= ttf
->glyphCount())
1627 sal_uInt32 hmtxlength
;
1628 const sal_uInt8
* hmtx
= ttf
->table(O_hmtx
, hmtxlength
);
1633 sal_uInt32 glyflength
;
1634 const sal_uInt8
* glyf
= ttf
->table(O_glyf
, glyflength
);
1637 /* #127161# check the glyph offsets */
1638 sal_uInt32 nNextOffset
= ttf
->glyphOffset(glyphID
+ 1);
1639 if (nNextOffset
> glyflength
)
1642 sal_uInt32 nOffset
= ttf
->glyphOffset(glyphID
);
1643 if (nOffset
> nNextOffset
)
1646 sal_uInt32 length
= nNextOffset
- nOffset
;
1648 std::unique_ptr
<GlyphData
> d(new GlyphData
);
1651 const sal_uInt8
* srcptr
= glyf
+ ttf
->glyphOffset(glyphID
);
1652 const size_t nChunkLen
= ((length
+ 1) & ~1);
1653 d
->ptr
.reset(new sal_uInt8
[nChunkLen
]);
1654 memcpy(d
->ptr
.get(), srcptr
, length
);
1655 memset(d
->ptr
.get() + length
, 0, nChunkLen
- length
);
1656 d
->compflag
= (GetInt16( srcptr
, 0 ) < 0);
1659 d
->compflag
= false;
1662 d
->glyphID
= glyphID
;
1663 d
->nbytes
= static_cast<sal_uInt16
>((length
+ 1) & ~1);
1665 /* now calculate npoints and ncontours */
1666 std::vector
<ControlPoint
> cp
;
1667 n
= GetTTGlyphPoints(ttf
, glyphID
, cp
);
1671 for (int i
= 0; i
< n
; i
++)
1673 if (cp
[i
].flags
& 0x8000)
1676 d
->npoints
= static_cast<sal_uInt16
>(n
);
1677 d
->ncontours
= static_cast<sal_uInt16
>(m
);
1683 /* get advance width and left sidebearing */
1684 sal_uInt32 nAwOffset
;
1685 sal_uInt32 nLsboffset
;
1686 if (glyphID
< ttf
->horzMetricCount()) {
1687 nAwOffset
= 4 * glyphID
;
1688 nLsboffset
= 4 * glyphID
+ 2;
1690 nAwOffset
= 4 * (ttf
->horzMetricCount() - 1);
1691 nLsboffset
= (ttf
->horzMetricCount() * 4) + ((glyphID
- ttf
->horzMetricCount()) * 2);
1694 if (nAwOffset
+ 2 <= hmtxlength
)
1695 d
->aw
= GetUInt16(hmtx
, nAwOffset
);
1698 SAL_WARN("vcl.fonts", "hmtx offset " << nAwOffset
<< " not available");
1701 if (nLsboffset
+ 2 <= hmtxlength
)
1702 d
->lsb
= GetInt16(hmtx
, nLsboffset
);
1705 SAL_WARN("vcl.fonts", "hmtx offset " << nLsboffset
<< " not available");
1712 void GetTTNameRecords(AbstractTrueTypeFont
const *ttf
, std::vector
<NameRecord
>& nr
)
1714 sal_uInt32 nTableSize
;
1715 const sal_uInt8
* table
= ttf
->table(O_name
, nTableSize
);
1721 #if OSL_DEBUG_LEVEL > 1
1722 SAL_WARN("vcl.fonts", "O_name table too small.");
1727 sal_uInt16 n
= GetUInt16(table
, 2);
1728 sal_uInt32 nStrBase
= GetUInt16(table
, 4);
1733 const sal_uInt32 remaining_table_size
= nTableSize
-6;
1734 const sal_uInt32 nMinRecordSize
= 12;
1735 const sal_uInt32 nMaxRecords
= remaining_table_size
/ nMinRecordSize
;
1736 if (n
> nMaxRecords
)
1738 SAL_WARN("vcl.fonts", "Parsing error in " << OUString::createFromAscii(ttf
->fileName()) <<
1739 ": " << nMaxRecords
<< " max possible entries, but " <<
1740 n
<< " claimed, truncating");
1746 for (i
= 0; i
< n
; i
++) {
1747 sal_uInt32 nLargestFixedOffsetPos
= 6 + 10 + 12 * i
;
1748 sal_uInt32 nMinSize
= nLargestFixedOffsetPos
+ sizeof(sal_uInt16
);
1749 if (nMinSize
> nTableSize
)
1751 SAL_WARN( "vcl.fonts", "Font " << OUString::createFromAscii(ttf
->fileName()) << " claimed to have "
1752 << n
<< " name records, but only space for " << i
);
1756 nr
[i
].platformID
= GetUInt16(table
, 6 + 0 + 12 * i
);
1757 nr
[i
].encodingID
= GetUInt16(table
, 6 + 2 + 12 * i
);
1758 nr
[i
].languageID
= LanguageType(GetUInt16(table
, 6 + 4 + 12 * i
));
1759 nr
[i
].nameID
= GetUInt16(table
, 6 + 6 + 12 * i
);
1760 sal_uInt16 slen
= GetUInt16(table
, 6 + 8 + 12 * i
);
1761 sal_uInt32 nStrOffset
= GetUInt16(table
, nLargestFixedOffsetPos
);
1763 if (nStrBase
+ nStrOffset
+ slen
>= nTableSize
)
1766 const sal_uInt32 rec_string
= nStrBase
+ nStrOffset
;
1767 const size_t available_space
= rec_string
> nTableSize
? 0 : (nTableSize
- rec_string
);
1768 if (slen
<= available_space
)
1770 nr
[i
].sptr
.resize(slen
);
1771 memcpy(nr
[i
].sptr
.data(), table
+ rec_string
, slen
);
1774 // some fonts have 3.0 names => fix them to 3.1
1775 if( (nr
[i
].platformID
== 3) && (nr
[i
].encodingID
== 0) )
1776 nr
[i
].encodingID
= 1;
1780 template<size_t N
> static void
1781 append(std::bitset
<N
> & rSet
, size_t const nOffset
, sal_uInt32
const nValue
)
1783 for (size_t i
= 0; i
< 32; ++i
)
1785 rSet
.set(nOffset
+ i
, (nValue
& (1U << i
)) != 0);
1790 std::optional
<std::bitset
<UnicodeCoverage::MAX_UC_ENUM
>> &rUnicodeRange
,
1791 std::optional
<std::bitset
<CodePageCoverage::MAX_CP_ENUM
>> &rCodePageRange
,
1792 const unsigned char* pTable
, size_t nLength
)
1795 // parse OS/2 header
1796 if (nLength
>= OS2_Legacy_length
)
1798 rUnicodeRange
= std::bitset
<UnicodeCoverage::MAX_UC_ENUM
>();
1799 append(*rUnicodeRange
, 0, GetUInt32(pTable
, OS2_ulUnicodeRange1_offset
));
1800 append(*rUnicodeRange
, 32, GetUInt32(pTable
, OS2_ulUnicodeRange2_offset
));
1801 append(*rUnicodeRange
, 64, GetUInt32(pTable
, OS2_ulUnicodeRange3_offset
));
1802 append(*rUnicodeRange
, 96, GetUInt32(pTable
, OS2_ulUnicodeRange4_offset
));
1804 if (nLength
>= OS2_V1_length
)
1806 rCodePageRange
= std::bitset
<CodePageCoverage::MAX_CP_ENUM
>();
1807 append(*rCodePageRange
, 0, GetUInt32(pTable
, OS2_ulCodePageRange1_offset
));
1808 append(*rCodePageRange
, 32, GetUInt32(pTable
, OS2_ulCodePageRange2_offset
));
1816 int TestFontSubset(const void* data
, sal_uInt32 size
)
1819 vcl::TrueTypeFont
* pTTF
= nullptr;
1820 if (OpenTTFontBuffer(data
, size
, 0, &pTTF
) == vcl::SFErrCodes::Ok
)
1822 vcl::TTGlobalFontInfo aInfo
;
1823 GetTTGlobalFontInfo(pTTF
, &aInfo
);
1825 sal_uInt16 aGlyphIds
[ 256 ] = {};
1826 sal_uInt8 aEncoding
[ 256 ] = {};
1828 for (sal_uInt16 c
= 32; c
< 256; ++c
)
1831 aGlyphIds
[c
] = c
- 31;
1834 std::vector
<sal_uInt8
> aBuffer
;
1835 CreateTTFromTTGlyphs(pTTF
, aBuffer
, aGlyphIds
, aEncoding
, 256);
1839 CloseTTFont( pTTF
);
1847 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */