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>
61 /*- module identification */
63 const char * const modname
= "SunTypeTools-TT";
64 const char * const modver
= "1.0";
65 const char * const modextra
= "gelf";
67 /*- private functions, constants and data types */
71 enum PathSegmentType
{
86 explicit PSPathElement( PathSegmentType i_eType
) : type( i_eType
),
93 PSPathElement() : type( PS_NOOP
),
101 /*- In horizontal writing mode right sidebearing is calculated using this formula
102 *- rsb = aw - (lsb + xMax - xMin) -*/
103 struct TTGlyphMetrics
{
108 sal_uInt16 aw
; /*- Advance Width (horizontal writing mode) */
109 sal_Int16 lsb
; /*- Left sidebearing (horizontal writing mode) */
110 sal_uInt16 ah
; /*- advance height (vertical writing mode) */
113 constexpr int HFORMAT_LINELEN
= 64;
117 HexFmt(SvStream
*outf
) : o(outf
) {}
125 void BlockWrite(const void *ptr
, sal_uInt32 size
);
129 char buffer
[HFORMAT_LINELEN
];
136 GlyphOffsets(sal_uInt8
*sfntP
, sal_uInt32 sfntLen
);
137 sal_uInt32 nGlyphs
; /* number of glyphs in the font + 1 */
138 std::unique_ptr
<sal_uInt32
[]> offs
; /* array of nGlyphs offsets */
143 /*- Data access methods for data stored in big-endian format */
144 static sal_Int16
GetInt16(const sal_uInt8
*ptr
, size_t offset
)
147 assert(ptr
!= nullptr);
149 t
= (ptr
+offset
)[0] << 8 | (ptr
+offset
)[1];
154 static sal_uInt16
GetUInt16(const sal_uInt8
*ptr
, size_t offset
)
157 assert(ptr
!= nullptr);
159 t
= (ptr
+offset
)[0] << 8 | (ptr
+offset
)[1];
164 static sal_Int32
GetInt32(const sal_uInt8
*ptr
, size_t offset
)
167 assert(ptr
!= nullptr);
169 t
= (ptr
+offset
)[0] << 24 | (ptr
+offset
)[1] << 16 |
170 (ptr
+offset
)[2] << 8 | (ptr
+offset
)[3];
175 static sal_uInt32
GetUInt32(const sal_uInt8
*ptr
, size_t offset
)
178 assert(ptr
!= nullptr);
180 t
= (ptr
+offset
)[0] << 24 | (ptr
+offset
)[1] << 16 |
181 (ptr
+offset
)[2] << 8 | (ptr
+offset
)[3];
186 static F16Dot16
fixedMul(F16Dot16 a
, F16Dot16 b
)
188 return fix16_mul(a
, b
);
191 static F16Dot16
fixedDiv(F16Dot16 a
, F16Dot16 b
)
193 return fix16_div(a
, b
);
196 /*- returns a * b / c -*/
197 /* XXX provide a real implementation that preserves accuracy */
198 static F16Dot16
fixedMulDiv(F16Dot16 a
, F16Dot16 b
, F16Dot16 c
)
200 F16Dot16 res
= fixedMul(a
, b
);
201 return fixedDiv(res
, c
);
204 /*- Translate units from TT to PS (standard 1/1000) -*/
205 static int XUnits(int unitsPerEm
, int n
)
207 return (n
* 1000) / unitsPerEm
;
210 static char toHex(sal_uInt8 nIndex
)
212 /* Hex Formatter functions */
213 static const char HexChars
[] = "0123456789ABCDEF";
214 assert(nIndex
< SAL_N_ELEMENTS(HexChars
));
215 return HexChars
[nIndex
];
222 size_t nWritten
= o
->WriteBytes(buffer
, bufpos
);
223 bRet
= nWritten
== bufpos
;
229 void HexFmt::OpenString()
231 o
->WriteOString("<\n");
234 void HexFmt::CloseString()
237 o
->WriteOString("00\n>\n");
240 void HexFmt::BlockWrite(const void *ptr
, sal_uInt32 size
)
242 if (total
+ size
> 65534) {
249 for (sal_uInt32 i
= 0; i
< size
; ++i
) {
250 sal_uInt8 Ch
= static_cast<sal_uInt8
const *>(ptr
)[i
];
251 buffer
[bufpos
++] = toHex(Ch
>> 4);
252 buffer
[bufpos
++] = toHex(Ch
& 0xF);
253 if (bufpos
== HFORMAT_LINELEN
) {
255 o
->WriteOString("\n");
262 /* Outline Extraction functions */
264 /* fills the aw and lsb entries of the TTGlyphMetrics structure from hmtx table -*/
265 static void GetMetrics(AbstractTrueTypeFont
const *ttf
, sal_uInt32 glyphID
, TTGlyphMetrics
*metrics
)
268 const sal_uInt8
* table
= ttf
->table(O_hmtx
, nSize
);
270 metrics
->aw
= metrics
->lsb
= metrics
->ah
= 0;
271 if (!table
|| !ttf
->horzMetricCount())
274 if (glyphID
< ttf
->horzMetricCount())
276 metrics
->aw
= GetUInt16(table
, 4 * glyphID
);
277 metrics
->lsb
= GetInt16(table
, 4 * glyphID
+ 2);
281 metrics
->aw
= GetUInt16(table
, 4 * (ttf
->horzMetricCount() - 1));
282 metrics
->lsb
= GetInt16(table
+ ttf
->horzMetricCount() * 4, (glyphID
- ttf
->horzMetricCount()) * 2);
285 table
= ttf
->table(O_vmtx
, nSize
);
286 if (!table
|| !ttf
->vertMetricCount())
289 if (glyphID
< ttf
->vertMetricCount())
290 metrics
->ah
= GetUInt16(table
, 4 * glyphID
);
292 metrics
->ah
= GetUInt16(table
, 4 * (ttf
->vertMetricCount() - 1));
295 static int GetTTGlyphOutline(AbstractTrueTypeFont
*, sal_uInt32
, std::vector
<ControlPoint
>&, TTGlyphMetrics
*, std::vector
< sal_uInt32
>* );
297 /* returns the number of control points, allocates the pointArray */
298 static int GetSimpleTTOutline(AbstractTrueTypeFont
const *ttf
, sal_uInt32 glyphID
,
299 std::vector
<ControlPoint
>& pointArray
, TTGlyphMetrics
*metrics
)
301 sal_uInt32 nTableSize
;
302 const sal_uInt8
* table
= ttf
->table(O_glyf
, nTableSize
);
308 if (glyphID
>= ttf
->glyphCount())
311 sal_uInt32 nGlyphOffset
= ttf
->glyphOffset(glyphID
);
312 if (nGlyphOffset
> nTableSize
)
315 const sal_uInt8
* ptr
= table
+ nGlyphOffset
;
316 const sal_uInt32 nMaxGlyphSize
= nTableSize
- nGlyphOffset
;
317 constexpr sal_uInt32 nContourOffset
= 10;
318 if (nMaxGlyphSize
< nContourOffset
)
321 const sal_Int16 numberOfContours
= GetInt16(ptr
, GLYF_numberOfContours_offset
);
322 if( numberOfContours
<= 0 ) /*- glyph is not simple */
325 const sal_Int32 nMaxContours
= (nMaxGlyphSize
- nContourOffset
)/2;
326 if (numberOfContours
> nMaxContours
)
329 if (metrics
) { /*- GetCompoundTTOutline() calls this function with NULL metrics -*/
330 metrics
->xMin
= GetInt16(ptr
, GLYF_xMin_offset
);
331 metrics
->yMin
= GetInt16(ptr
, GLYF_yMin_offset
);
332 metrics
->xMax
= GetInt16(ptr
, GLYF_xMax_offset
);
333 metrics
->yMax
= GetInt16(ptr
, GLYF_yMax_offset
);
334 GetMetrics(ttf
, glyphID
, metrics
);
337 /* determine the last point and be extra safe about it. But probably this code is not needed */
338 sal_uInt16 lastPoint
=0;
339 for (i
=0; i
<numberOfContours
; i
++)
341 const sal_uInt16 t
= GetUInt16(ptr
, nContourOffset
+ i
* 2);
346 sal_uInt32 nInstLenOffset
= nContourOffset
+ numberOfContours
* 2;
347 if (nInstLenOffset
+ 2 > nMaxGlyphSize
)
349 sal_uInt16 instLen
= GetUInt16(ptr
, nInstLenOffset
);
351 sal_uInt32 nOffset
= nContourOffset
+ 2 * numberOfContours
+ 2 + instLen
;
352 if (nOffset
> nMaxGlyphSize
)
354 const sal_uInt8
* p
= ptr
+ nOffset
;
356 sal_uInt32 nBytesRemaining
= nMaxGlyphSize
- nOffset
;
357 const sal_uInt32 palen
= lastPoint
+1;
359 //at a minimum its one byte per entry
360 if (palen
> nBytesRemaining
|| lastPoint
> nBytesRemaining
-1)
362 SAL_WARN("vcl.fonts", "Font " << OUString::createFromAscii(ttf
->fileName()) <<
363 "claimed a palen of "
364 << palen
<< " but max bytes remaining is " << nBytesRemaining
);
368 std::vector
<ControlPoint
> pa(palen
);
371 while (i
<= lastPoint
) {
372 if (!nBytesRemaining
)
374 SAL_WARN("vcl.fonts", "short read");
377 sal_uInt8 flag
= *p
++;
379 pa
[i
++].flags
= static_cast<sal_uInt32
>(flag
);
380 if (flag
& 8) { /*- repeat flag */
381 if (!nBytesRemaining
)
383 SAL_WARN("vcl.fonts", "short read");
388 // coverity[tainted_data : FALSE] - i > lastPoint extra checks the n loop bound
389 for (j
=0; j
<n
; j
++) {
390 if (i
> lastPoint
) { /*- if the font is really broken */
393 pa
[i
++].flags
= flag
;
398 /*- Process the X coordinate */
400 for (i
= 0; i
<= lastPoint
; i
++) {
401 if (pa
[i
].flags
& 0x02) {
402 if (!nBytesRemaining
)
404 SAL_WARN("vcl.fonts", "short read");
407 if (pa
[i
].flags
& 0x10) {
408 z
+= static_cast<int>(*p
++);
410 z
-= static_cast<int>(*p
++);
413 } else if ( !(pa
[i
].flags
& 0x10)) {
414 if (nBytesRemaining
< 2)
416 SAL_WARN("vcl.fonts", "short read");
421 nBytesRemaining
-= 2;
423 pa
[i
].x
= static_cast<sal_Int16
>(z
);
426 /*- Process the Y coordinate */
428 for (i
= 0; i
<= lastPoint
; i
++) {
429 if (pa
[i
].flags
& 0x04) {
430 if (!nBytesRemaining
)
432 SAL_WARN("vcl.fonts", "short read");
435 if (pa
[i
].flags
& 0x20) {
441 } else if ( !(pa
[i
].flags
& 0x20)) {
442 if (nBytesRemaining
< 2)
444 SAL_WARN("vcl.fonts", "short read");
449 nBytesRemaining
-= 2;
451 pa
[i
].y
= static_cast<sal_Int16
>(z
);
454 for (i
=0; i
<numberOfContours
; i
++) {
455 sal_uInt16 offset
= GetUInt16(ptr
, 10 + i
* 2);
456 SAL_WARN_IF(offset
>= palen
, "vcl.fonts", "Font " << OUString::createFromAscii(ttf
->fileName()) <<
457 " contour " << i
<< " claimed an illegal offset of "
458 << offset
<< " but max offset is " << palen
-1);
461 pa
[offset
].flags
|= 0x00008000; /*- set the end contour flag */
464 pointArray
= std::move(pa
);
465 return lastPoint
+ 1;
468 static F16Dot16
fromF2Dot14(sal_Int16 n
)
470 // Avoid undefined shift of negative values prior to C++2a:
471 return sal_uInt32(n
) << 2;
474 static int GetCompoundTTOutline(AbstractTrueTypeFont
*ttf
, sal_uInt32 glyphID
, std::vector
<ControlPoint
>& pointArray
,
475 TTGlyphMetrics
*metrics
, std::vector
<sal_uInt32
>& glyphlist
)
477 sal_uInt16 flags
, index
;
479 sal_uInt32 nTableSize
;
480 const sal_uInt8
* table
= ttf
->table(O_glyf
, nTableSize
);
481 std::vector
<ControlPoint
> myPoints
;
482 std::vector
<ControlPoint
> nextComponent
;
484 F16Dot16 a
= 0x10000, b
= 0, c
= 0, d
= 0x10000, m
, n
, abs1
, abs2
, abs3
;
488 if (glyphID
>= ttf
->glyphCount())
491 sal_uInt32 nGlyphOffset
= ttf
->glyphOffset(glyphID
);
492 if (nGlyphOffset
> nTableSize
)
495 const sal_uInt8
* ptr
= table
+ nGlyphOffset
;
496 sal_uInt32 nAvailableBytes
= nTableSize
- nGlyphOffset
;
498 if (GLYF_numberOfContours_offset
+ 2 > nAvailableBytes
)
501 if (GetInt16(ptr
, GLYF_numberOfContours_offset
) != -1) /* number of contours - glyph is not compound */
505 metrics
->xMin
= GetInt16(ptr
, GLYF_xMin_offset
);
506 metrics
->yMin
= GetInt16(ptr
, GLYF_yMin_offset
);
507 metrics
->xMax
= GetInt16(ptr
, GLYF_xMax_offset
);
508 metrics
->yMax
= GetInt16(ptr
, GLYF_yMax_offset
);
509 GetMetrics(ttf
, glyphID
, metrics
);
512 if (nAvailableBytes
< 10)
514 SAL_WARN("vcl.fonts", "short read");
519 nAvailableBytes
-= 10;
523 if (nAvailableBytes
< 4)
525 SAL_WARN("vcl.fonts", "short read");
528 flags
= GetUInt16(ptr
, 0);
529 /* printf("flags: 0x%X\n", flags); */
530 index
= GetUInt16(ptr
, 2);
532 nAvailableBytes
-= 4;
534 if( std::find( glyphlist
.begin(), glyphlist
.end(), index
) != glyphlist
.end() )
536 SAL_WARN("vcl.fonts", "Endless loop found in a compound glyph.");
538 #if OSL_DEBUG_LEVEL > 1
539 std::ostringstream oss
;
540 oss
<< index
<< " -> [";
541 for( const auto& rGlyph
: glyphlist
)
543 oss
<< (int) rGlyph
<< " ";
546 SAL_INFO("vcl.fonts", oss
.str());
552 glyphlist
.push_back( index
);
554 np
= GetTTGlyphOutline(ttf
, index
, nextComponent
, nullptr, &glyphlist
);
556 if( ! glyphlist
.empty() )
557 glyphlist
.pop_back();
561 /* XXX that probably indicates a corrupted font */
562 SAL_WARN("vcl.fonts", "An empty compound!");
563 /* assert(!"An empty compound"); */
567 if ((flags
& USE_MY_METRICS
) && metrics
)
568 GetMetrics(ttf
, index
, metrics
);
570 if (flags
& ARG_1_AND_2_ARE_WORDS
) {
571 if (nAvailableBytes
< 4)
573 SAL_WARN("vcl.fonts", "short read");
576 e
= GetInt16(ptr
, 0);
577 f
= GetInt16(ptr
, 2);
578 /* printf("ARG_1_AND_2_ARE_WORDS: %d %d\n", e & 0xFFFF, f & 0xFFFF); */
580 nAvailableBytes
-= 4;
582 if (nAvailableBytes
< 2)
584 SAL_WARN("vcl.fonts", "short read");
587 if (flags
& ARGS_ARE_XY_VALUES
) { /* args are signed */
588 e
= static_cast<sal_Int8
>(*ptr
++);
589 f
= static_cast<sal_Int8
>(*ptr
++);
590 /* printf("ARGS_ARE_XY_VALUES: %d %d\n", e & 0xFF, f & 0xFF); */
591 } else { /* args are unsigned */
592 /* printf("!ARGS_ARE_XY_VALUES\n"); */
596 nAvailableBytes
-= 2;
602 if (flags
& WE_HAVE_A_SCALE
) {
603 if (nAvailableBytes
< 2)
605 SAL_WARN("vcl.fonts", "short read");
608 a
= fromF2Dot14(GetInt16(ptr
, 0));
611 nAvailableBytes
-= 2;
612 } else if (flags
& WE_HAVE_AN_X_AND_Y_SCALE
) {
613 if (nAvailableBytes
< 4)
615 SAL_WARN("vcl.fonts", "short read");
618 a
= fromF2Dot14(GetInt16(ptr
, 0));
619 d
= fromF2Dot14(GetInt16(ptr
, 2));
621 nAvailableBytes
-= 4;
622 } else if (flags
& WE_HAVE_A_TWO_BY_TWO
) {
623 if (nAvailableBytes
< 8)
625 SAL_WARN("vcl.fonts", "short read");
628 a
= fromF2Dot14(GetInt16(ptr
, 0));
629 b
= fromF2Dot14(GetInt16(ptr
, 2));
630 c
= fromF2Dot14(GetInt16(ptr
, 4));
631 d
= fromF2Dot14(GetInt16(ptr
, 6));
633 nAvailableBytes
-= 8;
636 abs1
= (a
< 0) ? -a
: a
;
637 abs2
= (b
< 0) ? -b
: b
;
638 m
= std::max(abs1
, abs2
);
640 if (abs3
< 0) abs3
= -abs3
;
641 if (abs3
<= 33) m
*= 2;
643 abs1
= (c
< 0) ? -c
: c
;
644 abs2
= (d
< 0) ? -d
: d
;
645 n
= std::max(abs1
, abs2
);
647 if (abs3
< 0) abs3
= -abs3
;
648 if (abs3
<= 33) n
*= 2;
650 SAL_WARN_IF(np
&& (!m
|| !n
), "vcl.fonts", "Parsing error in " << OUString::createFromAscii(ttf
->fileName()) <<
653 if (m
!= 0 && n
!= 0) {
654 for (i
=0; i
<np
; i
++) {
657 cp
.flags
= nextComponent
[i
].flags
;
658 const sal_uInt16 x
= nextComponent
[i
].x
;
659 const sal_uInt16 y
= nextComponent
[i
].y
;
660 t
= o3tl::saturating_add(o3tl::saturating_add(fixedMulDiv(a
, x
<< 16, m
), fixedMulDiv(c
, y
<< 16, m
)), sal_Int32(sal_uInt16(e
) << 16));
661 cp
.x
= static_cast<sal_Int16
>(fixedMul(t
, m
) >> 16);
662 t
= o3tl::saturating_add(o3tl::saturating_add(fixedMulDiv(b
, x
<< 16, n
), fixedMulDiv(d
, y
<< 16, n
)), sal_Int32(sal_uInt16(f
) << 16));
663 cp
.y
= static_cast<sal_Int16
>(fixedMul(t
, n
) >> 16);
665 myPoints
.push_back( cp
);
669 if (myPoints
.size() > SAL_MAX_UINT16
) {
670 SAL_WARN("vcl.fonts", "number of points has to be limited to max value GlyphData::npoints can contain, abandon effort");
675 } while (flags
& MORE_COMPONENTS
);
677 // #i123417# some fonts like IFAOGrec have no outline points in some compound glyphs
678 // so this unlikely but possible scenario should be handled gracefully
679 if( myPoints
.empty() )
682 np
= myPoints
.size();
684 pointArray
= std::move(myPoints
);
689 /* NOTE: GetTTGlyphOutline() returns -1 if the glyphID is incorrect,
690 * but Get{Simple|Compound}GlyphOutline returns 0 in such a case.
692 * NOTE: glyphlist is the stack of glyphs traversed while constructing
693 * a composite glyph. This is a safeguard against endless recursion
694 * in corrupted fonts.
696 static int GetTTGlyphOutline(AbstractTrueTypeFont
*ttf
, sal_uInt32 glyphID
, std::vector
<ControlPoint
>& pointArray
, TTGlyphMetrics
*metrics
, std::vector
< sal_uInt32
>* glyphlist
)
698 sal_uInt32 glyflength
;
699 const sal_uInt8
*table
= ttf
->table(O_glyf
, glyflength
);
700 sal_Int16 numberOfContours
;
705 memset(metrics
, 0, sizeof(TTGlyphMetrics
));
707 if (glyphID
>= ttf
->glyphCount())
710 sal_uInt32 nNextOffset
= ttf
->glyphOffset(glyphID
+ 1);
711 if (nNextOffset
> glyflength
)
714 sal_uInt32 nOffset
= ttf
->glyphOffset(glyphID
);
715 if (nOffset
> nNextOffset
)
718 int length
= nNextOffset
- nOffset
;
719 if (length
== 0) { /*- empty glyphs still have hmtx and vmtx metrics values */
720 if (metrics
) GetMetrics(ttf
, glyphID
, metrics
);
724 const sal_uInt8
* ptr
= table
+ nOffset
;
725 const sal_uInt32 nMaxGlyphSize
= glyflength
- nOffset
;
727 if (nMaxGlyphSize
< 2)
730 numberOfContours
= GetInt16(ptr
, 0);
732 if (numberOfContours
>= 0)
734 res
= GetSimpleTTOutline(ttf
, glyphID
, pointArray
, metrics
);
738 std::vector
< sal_uInt32
> aPrivList
{ glyphID
};
739 res
= GetCompoundTTOutline(ttf
, glyphID
, pointArray
, metrics
, glyphlist
? *glyphlist
: aPrivList
);
745 /*- returns the number of items in the path -*/
747 static int BSplineToPSPath(ControlPoint
const *srcA
, int srcCount
, std::unique_ptr
<PSPathElement
[]>& path
)
749 std::vector
< PSPathElement
> aPathList
;
751 PSPathElement
p( PS_NOOP
);
753 int x0
= 0, y0
= 0, x1
= 0, y1
= 0, x2
, y2
, curx
, cury
;
754 bool lastOff
= false; /*- last point was off-contour */
755 int scflag
= 1; /*- start contour flag */
756 bool ecflag
= false; /*- end contour flag */
757 int cp
= 0; /*- current point */
758 int StartContour
= 0, EndContour
= 1;
762 /* if (srcCount > 0) for(;;) */
763 while (srcCount
> 0) { /*- srcCount does not get changed inside the loop. */
767 while (!(srcA
[l
].flags
& 0x8000)) l
++;
769 if (StartContour
== EndContour
) {
770 if (cp
+ 1 < srcCount
) {
777 p
= PSPathElement(PS_MOVETO
);
778 if (!(srcA
[cp
].flags
& 1)) {
779 if (!(srcA
[EndContour
].flags
& 1)) {
780 p
.x1
= x0
= (srcA
[cp
].x
+ srcA
[EndContour
].x
+ 1) / 2;
781 p
.y1
= y0
= (srcA
[cp
].y
+ srcA
[EndContour
].y
+ 1) / 2;
783 p
.x1
= x0
= srcA
[EndContour
].x
;
784 p
.y1
= y0
= srcA
[EndContour
].y
;
787 p
.x1
= x0
= srcA
[cp
].x
;
788 p
.y1
= y0
= srcA
[cp
].y
;
791 aPathList
.push_back( p
);
799 if (srcA
[cp
].flags
& 1)
803 p
= PSPathElement(PS_CURVETO
);
804 p
.x1
= x0
+ (2 * (x1
- x0
) + 1) / 3;
805 p
.y1
= y0
+ (2 * (y1
- y0
) + 1) / 3;
806 p
.x2
= x1
+ (curx
- x1
+ 1) / 3;
807 p
.y2
= y1
+ (cury
- y1
+ 1) / 3;
810 aPathList
.push_back( p
);
814 if (x0
!= curx
|| y0
!= cury
)
815 { /* eliminate empty lines */
816 p
= PSPathElement(PS_LINETO
);
819 aPathList
.push_back( p
);
822 x0
= curx
; y0
= cury
; lastOff
= false;
828 x2
= (x1
+ curx
+ 1) / 2;
829 y2
= (y1
+ cury
+ 1) / 2;
830 p
= PSPathElement(PS_CURVETO
);
831 p
.x1
= x0
+ (2 * (x1
- x0
) + 1) / 3;
832 p
.y1
= y0
+ (2 * (y1
- y0
) + 1) / 3;
833 p
.x2
= x1
+ (x2
- x1
+ 1) / 3;
834 p
.y2
= y1
+ (y2
- y1
+ 1) / 3;
837 aPathList
.push_back( p
);
839 x1
= curx
; y1
= cury
;
841 x1
= curx
; y1
= cury
;
847 aPathList
.emplace_back(PS_CLOSEPATH
);
851 if (cp
>= srcCount
) break;
855 if (cp
== EndContour
) {
863 if( (nPathCount
= static_cast<int>(aPathList
.size())) > 0)
865 path
.reset(new PSPathElement
[nPathCount
]);
866 memcpy( path
.get(), aPathList
.data(), nPathCount
* sizeof(PSPathElement
) );
872 /*- Extracts a string from the name table and allocates memory for it -*/
874 static OString
nameExtract( const sal_uInt8
* name
, int nTableSize
, int n
, int dbFlag
, OUString
* ucs2result
)
877 const sal_uInt8
* ptr
= name
+ GetUInt16(name
, 4) + GetUInt16(name
+ 6, 12 * n
+ 10);
878 int len
= GetUInt16(name
+6, 12 * n
+ 8);
881 const sal_uInt8
* end_table
= name
+nTableSize
;
882 const int available_space
= ptr
> end_table
? 0 : (end_table
- ptr
);
883 if( (len
<= 0) || len
> available_space
)
893 res
.setLength(len
/2);
894 for (int i
= 0; i
< len
/2; i
++)
896 res
[i
] = *(ptr
+ i
* 2 + 1);
897 SAL_WARN_IF(res
[i
] == 0, "vcl.fonts", "font name is bogus");
901 OUStringBuffer
buf(len
/2);
902 buf
.setLength(len
/2);
903 for (int i
= 0; i
< len
/2; i
++ )
905 buf
[i
] = GetUInt16( ptr
, 2*i
);
906 SAL_WARN_IF(buf
[i
] == 0, "vcl.fonts", "font name is bogus");
908 *ucs2result
= buf
.makeStringAndClear();
912 memcpy(static_cast<void*>(const_cast<char*>(res
.getStr())), ptr
, len
);
915 return res
.makeStringAndClear();
918 static int findname( const sal_uInt8
*name
, sal_uInt16 n
, sal_uInt16 platformID
,
919 sal_uInt16 encodingID
, sal_uInt16 languageID
, sal_uInt16 nameID
)
921 if (n
== 0) return -1;
927 m1
= (platformID
<< 16) | encodingID
;
928 m2
= (languageID
<< 16) | nameID
;
931 const int i
= (l
+ r
) >> 1;
932 t1
= GetUInt32(name
+ 6, i
* 12 + 0);
933 t2
= GetUInt32(name
+ 6, i
* 12 + 4);
935 if (! ((m1
< t1
) || ((m1
== t1
) && (m2
< t2
)))) l
= i
+ 1;
936 if (! ((m1
> t1
) || ((m1
== t1
) && (m2
> t2
)))) r
= i
- 1;
946 /* XXX marlett.ttf uses (3, 0, 1033) instead of (3, 1, 1033) and does not have any Apple tables.
947 * Fix: if (3, 1, 1033) is not found - need to check for (3, 0, 1033)
949 * /d/fonts/ttzh_tw/Big5/Hanyi/ma6b5p uses (1, 0, 19) for English strings, instead of (1, 0, 0)
950 * and does not have (3, 1, 1033)
951 * Fix: if (1, 0, 0) and (3, 1, 1033) are not found need to look for (1, 0, *) - that will
952 * require a change in algorithm
954 * /d/fonts/fdltest/Korean/h2drrm has unsorted names and an unknown (to me) Mac LanguageID,
955 * but (1, 0, 1042) strings usable
956 * Fix: change algorithm, and use (1, 0, *) if both standard Mac and MS strings are not found
959 static void GetNames(AbstractTrueTypeFont
*t
)
961 sal_uInt32 nTableSize
;
962 const sal_uInt8
* table
= t
->table(O_name
, nTableSize
);
966 #if OSL_DEBUG_LEVEL > 1
967 SAL_WARN("vcl.fonts", "O_name table too small.");
972 sal_uInt16 n
= GetUInt16(table
, 2);
974 /* simple sanity check for name table entry count */
975 const size_t nMinRecordSize
= 12;
976 const size_t nSpaceAvailable
= nTableSize
- 6;
977 const size_t nMaxRecords
= nSpaceAvailable
/nMinRecordSize
;
978 if (n
>= nMaxRecords
)
982 bool bPSNameOK
= true;
984 /* PostScript name: preferred Microsoft */
986 if ((r
= findname(table
, n
, 3, 1, 0x0409, 6)) != -1)
987 t
->psname
= nameExtract(table
, nTableSize
, r
, 1, nullptr);
988 if ( t
->psname
.isEmpty() && (r
= findname(table
, n
, 1, 0, 0, 6)) != -1)
989 t
->psname
= nameExtract(table
, nTableSize
, r
, 0, nullptr);
990 if ( t
->psname
.isEmpty() && (r
= findname(table
, n
, 3, 0, 0x0409, 6)) != -1)
992 // some symbol fonts like Marlett have a 3,0 name!
993 t
->psname
= nameExtract(table
, nTableSize
, r
, 1, nullptr);
995 // for embedded font in Ghostscript PDFs
996 if ( t
->psname
.isEmpty() && (r
= findname(table
, n
, 2, 2, 0, 6)) != -1)
998 t
->psname
= nameExtract(table
, nTableSize
, r
, 0, nullptr);
1000 if ( t
->psname
.isEmpty() )
1002 if (!t
->fileName().empty())
1004 const char* pReverse
= t
->fileName().data() + t
->fileName().length();
1005 /* take only last token of filename */
1006 while (pReverse
!= t
->fileName().data() && *pReverse
!= '/') pReverse
--;
1007 if(*pReverse
== '/') pReverse
++;
1008 int nReverseLen
= strlen(pReverse
);
1009 for (i
=nReverseLen
- 1; i
> 0; i
--)
1011 /*- Remove the suffix -*/
1012 if (*(pReverse
+ i
) == '.' ) {
1017 t
->psname
= OString(std::string_view(pReverse
, nReverseLen
));
1020 t
->psname
= "Unknown";
1023 /* Font family and subfamily names: preferred Apple */
1025 if ((r
= findname(table
, n
, 0, 0, 0, 1)) != -1)
1026 t
->family
= nameExtract(table
, nTableSize
, r
, 1, &t
->ufamily
);
1027 if ( t
->family
.isEmpty() && (r
= findname(table
, n
, 3, 1, 0x0409, 1)) != -1)
1028 t
->family
= nameExtract(table
, nTableSize
, r
, 1, &t
->ufamily
);
1029 if ( t
->family
.isEmpty() && (r
= findname(table
, n
, 1, 0, 0, 1)) != -1)
1030 t
->family
= nameExtract(table
, nTableSize
, r
, 0, nullptr);
1031 if ( t
->family
.isEmpty() && (r
= findname(table
, n
, 3, 1, 0x0411, 1)) != -1)
1032 t
->family
= nameExtract(table
, nTableSize
, r
, 1, &t
->ufamily
);
1033 if ( t
->family
.isEmpty() && (r
= findname(table
, n
, 3, 0, 0x0409, 1)) != -1)
1034 t
->family
= nameExtract(table
, nTableSize
, r
, 1, &t
->ufamily
);
1035 if ( t
->family
.isEmpty() )
1036 t
->family
= t
->psname
;
1038 t
->subfamily
.clear();
1039 t
->usubfamily
.clear();
1040 if ((r
= findname(table
, n
, 1, 0, 0, 2)) != -1)
1041 t
->subfamily
= nameExtract(table
, nTableSize
, r
, 0, &t
->usubfamily
);
1042 if ( t
->subfamily
.isEmpty() && (r
= findname(table
, n
, 3, 1, 0x0409, 2)) != -1)
1043 t
->subfamily
= nameExtract(table
, nTableSize
, r
, 1, &t
->usubfamily
);
1045 /* #i60349# sanity check psname
1046 * psname practically has to be 7bit ASCII and should not contain spaces
1047 * there is a class of broken fonts which do not fulfill that at all, so let's try
1048 * if the family name is 7bit ASCII and take it instead if so
1051 for( i
= 0; i
< t
->psname
.getLength() && bPSNameOK
; i
++ )
1052 if( t
->psname
[ i
] < 33 || (t
->psname
[ i
] & 0x80) )
1057 /* check if family is a suitable replacement */
1058 if( t
->ufamily
.isEmpty() && t
->family
.isEmpty() )
1061 bool bReplace
= true;
1063 for( i
= 0; i
< t
->ufamily
.getLength() && bReplace
; i
++ )
1064 if( t
->ufamily
[ i
] < 33 || t
->ufamily
[ i
] > 127 )
1068 t
->psname
= t
->family
;
1072 /*- Public functions */
1074 int CountTTCFonts(const char* fname
)
1080 if (sscanf(fname
, "/:FD:/%d%n", &nFD
, &n
) == 1 && fname
[n
] == '\0')
1082 lseek(nFD
, 0, SEEK_SET
);
1083 int nDupFd
= dup(nFD
);
1084 fd
= nDupFd
!= -1 ? fdopen(nDupFd
, "rb") : nullptr;
1088 fd
= fopen(fname
, "rb");
1094 sal_uInt8 buffer
[12];
1095 if (fread(buffer
, 1, 12, fd
) == 12) {
1096 if(GetUInt32(buffer
, 0) == T_ttcf
)
1097 nFonts
= GetUInt32(buffer
, 8);
1102 fseek(fd
, 0, SEEK_END
);
1103 sal_uInt64 fileSize
= ftell(fd
);
1105 //Feel free to calc the exact max possible number of fonts a file
1106 //could contain given its physical size. But this will clamp it to
1107 //a sane starting point
1108 //http://processingjs.nihongoresources.com/the_smallest_font/
1109 //https://github.com/grzegorzrolek/null-ttf
1110 const int nMaxFontsPossible
= fileSize
/ 528;
1111 if (nFonts
> nMaxFontsPossible
)
1113 SAL_WARN("vcl.fonts", "font file " << fname
<<" claims to have "
1114 << nFonts
<< " fonts, but only "
1115 << nMaxFontsPossible
<< " are possible");
1116 nFonts
= nMaxFontsPossible
;
1125 #if !defined(_WIN32)
1126 SFErrCodes
OpenTTFontFile(const char* fname
, sal_uInt32 facenum
, TrueTypeFont
** ttf
,
1127 const FontCharMapRef xCharMap
)
1133 if (!fname
|| !*fname
) return SFErrCodes::BadFile
;
1135 *ttf
= new TrueTypeFont(fname
, xCharMap
);
1137 return SFErrCodes::Memory
;
1139 if( (*ttf
)->fileName().empty() )
1141 ret
= SFErrCodes::Memory
;
1147 if (sscanf(fname
, "/:FD:/%d%n", &nFD
, &n
) == 1 && fname
[n
] == '\0')
1149 lseek(nFD
, 0, SEEK_SET
);
1153 fd
= open(fname
, O_RDONLY
);
1156 ret
= SFErrCodes::BadFile
;
1160 if (fstat(fd
, &st
) == -1) {
1161 ret
= SFErrCodes::FileIo
;
1165 (*ttf
)->fsize
= st
.st_size
;
1167 /* On Mac OS, most likely will happen if a Mac user renames a font file
1168 * to be .ttf when it's really a Mac resource-based font.
1169 * Size will be 0, but fonts smaller than 4 bytes would be broken anyway.
1171 if ((*ttf
)->fsize
== 0) {
1172 ret
= SFErrCodes::BadFile
;
1176 if (((*ttf
)->ptr
= static_cast<sal_uInt8
*>(mmap(nullptr, (*ttf
)->fsize
, PROT_READ
, MAP_SHARED
, fd
, 0))) == MAP_FAILED
) {
1177 ret
= SFErrCodes::Memory
;
1181 ret
= (*ttf
)->open(facenum
);
1184 if (fd
!= -1) close(fd
);
1185 if (ret
!= SFErrCodes::Ok
)
1194 SFErrCodes
OpenTTFontBuffer(const void* pBuffer
, sal_uInt32 nLen
, sal_uInt32 facenum
, TrueTypeFont
** ttf
,
1195 const FontCharMapRef xCharMap
)
1197 *ttf
= new TrueTypeFont(nullptr, xCharMap
);
1198 if( *ttf
== nullptr )
1199 return SFErrCodes::Memory
;
1201 (*ttf
)->fsize
= nLen
;
1202 (*ttf
)->ptr
= const_cast<sal_uInt8
*>(static_cast<sal_uInt8
const *>(pBuffer
));
1204 SFErrCodes ret
= (*ttf
)->open(facenum
);
1205 if (ret
!= SFErrCodes::Ok
)
1215 bool withinBounds(sal_uInt32 tdoffset
, sal_uInt32 moreoffset
, sal_uInt32 len
, sal_uInt32 available
)
1218 if (o3tl::checked_add(tdoffset
, moreoffset
, result
))
1220 if (o3tl::checked_add(result
, len
, result
))
1222 return result
<= available
;
1226 AbstractTrueTypeFont::AbstractTrueTypeFont(const char* pFileName
, const FontCharMapRef xCharMap
)
1227 : m_nGlyphs(0xFFFFFFFF)
1231 , m_xCharMap(xCharMap
)
1232 , m_bMicrosoftSymbolEncoded(false)
1235 m_sFileName
= pFileName
;
1238 AbstractTrueTypeFont::~AbstractTrueTypeFont()
1242 TrueTypeFont::TrueTypeFont(const char* pFileName
, const FontCharMapRef xCharMap
)
1243 : AbstractTrueTypeFont(pFileName
, xCharMap
)
1250 TrueTypeFont::~TrueTypeFont()
1252 #if !defined(_WIN32)
1253 if (!fileName().empty())
1258 void CloseTTFont(TrueTypeFont
* ttf
) { delete ttf
; }
1260 SFErrCodes
AbstractTrueTypeFont::initialize()
1262 SFErrCodes ret
= indexGlyphData();
1263 if (ret
!= SFErrCodes::Ok
)
1268 return SFErrCodes::Ok
;
1271 sal_uInt32
AbstractTrueTypeFont::glyphOffset(sal_uInt32 glyphID
) const
1273 if (m_aGlyphOffsets
.empty()) // the O_CFF and Bitmap cases
1275 return m_aGlyphOffsets
[glyphID
];
1278 SFErrCodes
AbstractTrueTypeFont::indexGlyphData()
1280 if (!(hasTable(O_maxp
) && hasTable(O_head
) && hasTable(O_name
) && hasTable(O_cmap
)))
1281 return SFErrCodes::TtFormat
;
1283 sal_uInt32 table_size
;
1284 const sal_uInt8
* table
= this->table(O_maxp
, table_size
);
1285 m_nGlyphs
= table_size
>= 6 ? GetUInt16(table
, 4) : 0;
1287 table
= this->table(O_head
, table_size
);
1288 if (table_size
< HEAD_Length
)
1289 return SFErrCodes::TtFormat
;
1291 m_nUnitsPerEm
= GetUInt16(table
, HEAD_unitsPerEm_offset
);
1292 int indexfmt
= GetInt16(table
, HEAD_indexToLocFormat_offset
);
1294 if (((indexfmt
!= 0) && (indexfmt
!= 1)) || (m_nUnitsPerEm
<= 0))
1295 return SFErrCodes::TtFormat
;
1297 if (hasTable(O_glyf
) && (table
= this->table(O_loca
, table_size
))) /* TTF or TTF-OpenType */
1299 int k
= (table_size
/ (indexfmt
? 4 : 2)) - 1;
1300 if (k
< static_cast<int>(m_nGlyphs
)) /* Hack for broken Chinese fonts */
1303 m_aGlyphOffsets
.clear();
1304 m_aGlyphOffsets
.reserve(m_nGlyphs
+ 1);
1305 for (int i
= 0; i
<= static_cast<int>(m_nGlyphs
); ++i
)
1306 m_aGlyphOffsets
.push_back(indexfmt
? GetUInt32(table
, i
<< 2) : static_cast<sal_uInt32
>(GetUInt16(table
, i
<< 1)) << 1);
1308 else if (this->table(O_CFF
, table_size
)) /* PS-OpenType */
1310 int k
= (table_size
/ 2) - 1; /* set a limit here, presumably much lower than the table size, but establishes some sort of physical bound */
1311 if (k
< static_cast<int>(m_nGlyphs
))
1314 m_aGlyphOffsets
.clear();
1315 /* TODO: implement to get subsetting */
1318 // Bitmap font, accept for now.
1319 // TODO: We only need this for fonts with CBDT table since they usually
1320 // lack glyf or CFF table, the check should be more specific, or better
1321 // non-subsetting code should not be calling this.
1322 m_aGlyphOffsets
.clear();
1325 table
= this->table(O_hhea
, table_size
);
1326 m_nHorzMetrics
= (table
&& table_size
>= 36) ? GetUInt16(table
, 34) : 0;
1328 table
= this->table(O_vhea
, table_size
);
1329 m_nVertMetrics
= (table
&& table_size
>= 36) ? GetUInt16(table
, 34) : 0;
1331 if (!m_xCharMap
.is())
1333 table
= this->table(O_cmap
, table_size
);
1334 m_bMicrosoftSymbolEncoded
= HasMicrosoftSymbolCmap(table
, table_size
);
1337 m_bMicrosoftSymbolEncoded
= m_xCharMap
->isMicrosoftSymbolMap();
1339 return SFErrCodes::Ok
;
1342 SFErrCodes
TrueTypeFont::open(sal_uInt32 facenum
)
1345 return SFErrCodes::TtFormat
;
1348 sal_uInt32 length
, tag
;
1349 sal_uInt32 tdoffset
= 0; /* offset to TableDirectory in a TTC file. For TTF files is 0 */
1351 sal_uInt32 TTCTag
= GetInt32(ptr
, 0);
1353 if ((TTCTag
== 0x00010000) || (TTCTag
== T_true
)) {
1355 } else if (TTCTag
== T_otto
) { /* PS-OpenType font */
1357 } else if (TTCTag
== T_ttcf
) { /* TrueType collection */
1358 if (!withinBounds(12, 4 * facenum
, sizeof(sal_uInt32
), fsize
))
1359 return SFErrCodes::FontNo
;
1360 sal_uInt32 Version
= GetUInt32(ptr
, 4);
1361 if (Version
!= 0x00010000 && Version
!= 0x00020000) {
1362 return SFErrCodes::TtFormat
;
1364 if (facenum
>= GetUInt32(ptr
, 8))
1365 return SFErrCodes::FontNo
;
1366 tdoffset
= GetUInt32(ptr
, 12 + 4 * facenum
);
1368 return SFErrCodes::TtFormat
;
1371 if (withinBounds(tdoffset
, 0, 4 + sizeof(sal_uInt16
), fsize
))
1372 ntables
= GetUInt16(ptr
+ tdoffset
, 4);
1374 if (ntables
>= 128 || ntables
== 0)
1375 return SFErrCodes::TtFormat
;
1377 /* parse the tables */
1378 for (i
= 0; i
< static_cast<int>(ntables
); i
++)
1381 const sal_uInt32 nStart
= tdoffset
+ 12;
1382 const sal_uInt32 nOffset
= 16 * i
;
1383 if (withinBounds(nStart
, nOffset
, sizeof(sal_uInt32
), fsize
))
1384 tag
= GetUInt32(ptr
+ nStart
, nOffset
);
1386 tag
= static_cast<sal_uInt32
>(-1);
1388 case T_maxp
: nIndex
= O_maxp
; break;
1389 case T_glyf
: nIndex
= O_glyf
; break;
1390 case T_head
: nIndex
= O_head
; break;
1391 case T_loca
: nIndex
= O_loca
; break;
1392 case T_name
: nIndex
= O_name
; break;
1393 case T_hhea
: nIndex
= O_hhea
; break;
1394 case T_hmtx
: nIndex
= O_hmtx
; break;
1395 case T_cmap
: nIndex
= O_cmap
; break;
1396 case T_vhea
: nIndex
= O_vhea
; break;
1397 case T_vmtx
: nIndex
= O_vmtx
; break;
1398 case T_OS2
: nIndex
= O_OS2
; break;
1399 case T_post
: nIndex
= O_post
; break;
1400 case T_cvt
: nIndex
= O_cvt
; break;
1401 case T_prep
: nIndex
= O_prep
; break;
1402 case T_fpgm
: nIndex
= O_fpgm
; break;
1403 case T_CFF
: nIndex
= O_CFF
; break;
1404 default: nIndex
= -1; break;
1407 if ((nIndex
>= 0) && withinBounds(nStart
, nOffset
, 12 + sizeof(sal_uInt32
), fsize
))
1409 sal_uInt32 nTableOffset
= GetUInt32(ptr
+ nStart
, nOffset
+ 8);
1410 length
= GetUInt32(ptr
+ nStart
, nOffset
+ 12);
1411 m_aTableList
[nIndex
].pData
= ptr
+ nTableOffset
;
1412 m_aTableList
[nIndex
].nSize
= length
;
1416 /* Fixup offsets when only a TTC extract was provided */
1417 if (facenum
== sal_uInt32(~0))
1419 sal_uInt8
* pHead
= const_cast<sal_uInt8
*>(m_aTableList
[O_head
].pData
);
1421 return SFErrCodes::TtFormat
;
1423 /* limit Head candidate to TTC extract's limits */
1424 if (pHead
> ptr
+ (fsize
- 54))
1425 pHead
= ptr
+ (fsize
- 54);
1427 /* TODO: find better method than searching head table's magic */
1428 sal_uInt8
* p
= nullptr;
1429 for (p
= pHead
+ 12; p
> ptr
; --p
)
1431 if( p
[0]==0x5F && p
[1]==0x0F && p
[2]==0x3C && p
[3]==0xF5 ) {
1432 int nDelta
= (pHead
+ 12) - p
;
1434 for( int j
= 0; j
< NUM_TAGS
; ++j
)
1436 m_aTableList
[j
].pData
-= nDelta
;
1441 return SFErrCodes::TtFormat
;
1444 /* Check the table offsets after TTC correction */
1445 for (i
=0; i
<NUM_TAGS
; i
++) {
1446 /* sanity check: table must lay completely within the file
1447 * at this point one could check the checksum of all contained
1448 * tables, but this would be quite time intensive.
1449 * Try to fix tables, so we can cope with minor problems.
1452 if (m_aTableList
[i
].pData
< ptr
)
1454 #if OSL_DEBUG_LEVEL > 1
1455 SAL_WARN_IF(m_aTableList
[i
].pData
, "vcl.fonts", "font file " << fileName()
1456 << " has bad table offset "
1457 << (sal_uInt8
*)m_aTableList
[i
].pData
- ptr
1458 << "d (tagnum=" << i
<< ").");
1460 m_aTableList
[i
].nSize
= 0;
1461 m_aTableList
[i
].pData
= nullptr;
1463 else if (const_cast<sal_uInt8
*>(m_aTableList
[i
].pData
) + m_aTableList
[i
].nSize
> ptr
+ fsize
)
1465 sal_PtrDiff nMaxLen
= (ptr
+ fsize
) - m_aTableList
[i
].pData
;
1468 m_aTableList
[i
].nSize
= nMaxLen
;
1469 #if OSL_DEBUG_LEVEL > 1
1470 SAL_WARN("vcl.fonts", "font file " << fileName()
1471 << " has too big table (tagnum=" << i
<< ").");
1476 /* At this point TrueTypeFont is constructed, now need to verify the font format
1477 and read the basic font properties */
1479 return AbstractTrueTypeFont::initialize();
1482 int GetTTGlyphPoints(AbstractTrueTypeFont
*ttf
, sal_uInt32 glyphID
, std::vector
<ControlPoint
>& pointArray
)
1484 return GetTTGlyphOutline(ttf
, glyphID
, pointArray
, nullptr, nullptr);
1487 int GetTTGlyphComponents(AbstractTrueTypeFont
*ttf
, sal_uInt32 glyphID
, std::vector
< sal_uInt32
>& glyphlist
)
1491 if (glyphID
>= ttf
->glyphCount())
1494 sal_uInt32 glyflength
;
1495 const sal_uInt8
* glyf
= ttf
->table(O_glyf
, glyflength
);
1497 sal_uInt32 nNextOffset
= ttf
->glyphOffset(glyphID
+ 1);
1498 if (nNextOffset
> glyflength
)
1501 sal_uInt32 nOffset
= ttf
->glyphOffset(glyphID
);
1502 if (nOffset
> nNextOffset
)
1505 if (std::find(glyphlist
.begin(), glyphlist
.end(), glyphID
) != glyphlist
.end())
1507 SAL_WARN("vcl.fonts", "Endless loop found in a compound glyph.");
1511 glyphlist
.push_back( glyphID
);
1514 if (nOffset
== nNextOffset
)
1517 const auto* ptr
= glyf
+ nOffset
;
1518 sal_uInt32 nRemainingData
= glyflength
- nOffset
;
1520 if (nRemainingData
>= 10 && GetInt16(ptr
, 0) == -1) {
1521 sal_uInt16 flags
, index
;
1523 nRemainingData
-= 10;
1525 if (nRemainingData
< 4)
1527 SAL_WARN("vcl.fonts", "short read");
1530 flags
= GetUInt16(ptr
, 0);
1531 index
= GetUInt16(ptr
, 2);
1534 nRemainingData
-= 4;
1535 n
+= GetTTGlyphComponents(ttf
, index
, glyphlist
);
1537 sal_uInt32 nAdvance
;
1538 if (flags
& ARG_1_AND_2_ARE_WORDS
) {
1544 if (flags
& WE_HAVE_A_SCALE
) {
1546 } else if (flags
& WE_HAVE_AN_X_AND_Y_SCALE
) {
1548 } else if (flags
& WE_HAVE_A_TWO_BY_TWO
) {
1551 if (nRemainingData
< nAdvance
)
1553 SAL_WARN("vcl.fonts", "short read");
1557 nRemainingData
-= nAdvance
;
1558 } while (flags
& MORE_COMPONENTS
);
1564 SFErrCodes
CreateT3FromTTGlyphs(TrueTypeFont
*ttf
, SvStream
*outf
, const char *fname
,
1565 sal_uInt16
const *glyphArray
, sal_uInt8
*encoding
, int nGlyphs
,
1568 std::vector
<ControlPoint
> pa
;
1569 std::unique_ptr
<PSPathElement
[]> path
;
1572 const sal_uInt8
* table
= ttf
->table(O_head
, nSize
);
1573 TTGlyphMetrics metrics
;
1574 int UPEm
= ttf
->unitsPerEm();
1576 const char * const h01
= "%%!PS-AdobeFont-%d.%d-%d.%d\n";
1577 const char * const h02
= "%% Creator: %s %s %s\n";
1578 const char * const h09
= "%% Original font name: %s\n";
1580 const char * const h10
=
1582 "/PaintType 0 def\n"
1584 "/StrokeWidth 0 def\n";
1586 const char * const h11
= "/FontName (%s) cvn def\n";
1589 const char *h12 = "%/UniqueID %d def\n";
1591 const char * const h13
= "/FontMatrix [.001 0 0 .001 0 0] def\n";
1592 const char * const h14
= "/FontBBox [%d %d %d %d] def\n";
1594 const char * const h15
=
1595 "/Encoding 256 array def\n"
1596 " 0 1 255 {Encoding exch /.notdef put} for\n";
1598 const char * const h16
= " Encoding %d /glyph%d put\n";
1599 const char * const h17
= "/XUID [103 0 0 16#%08" SAL_PRIXUINT32
" %d 16#%08" SAL_PRIXUINT32
" 16#%08" SAL_PRIXUINT32
"] def\n";
1601 const char * const h30
= "/CharProcs %d dict def\n";
1602 const char * const h31
= " CharProcs begin\n";
1603 const char * const h32
= " /.notdef {} def\n";
1604 const char * const h33
= " /glyph%d {\n";
1605 const char * const h34
= " } bind def\n";
1606 const char * const h35
= " end\n";
1608 const char * const h40
=
1610 " exch /CharProcs get exch\n"
1611 " 2 copy known not\n"
1612 " {pop /.notdef} if\n"
1616 " 1 index /Encoding get exch get\n"
1617 " 1 index /BuildGlyph get exec\n"
1619 "currentdict end\n";
1621 const char * const h41
= "(%s) cvn exch definefont pop\n";
1623 if ((nGlyphs
<= 0) || (nGlyphs
> 256)) return SFErrCodes::GlyphNum
;
1624 if (!glyphArray
) return SFErrCodes::BadArg
;
1625 if (!fname
) fname
= ttf
->psname
.getStr();
1627 constexpr int bufmax
= 256;
1630 snprintf(buf
, bufmax
, h01
, GetInt16(table
, 0), GetUInt16(table
, 2), GetInt16(table
, 4), GetUInt16(table
, 6));
1631 outf
->WriteOString(buf
);
1632 snprintf(buf
, bufmax
, h02
, modname
, modver
, modextra
);
1633 outf
->WriteOString(buf
);
1634 snprintf(buf
, bufmax
, h09
, ttf
->psname
.getStr());
1635 outf
->WriteOString(buf
);
1637 snprintf(buf
, bufmax
, "%s", h10
);
1638 outf
->WriteOString(buf
);
1639 snprintf(buf
, bufmax
, h11
, fname
);
1640 outf
->WriteOString(buf
);
1641 /* snprintf(buf, bufmax, h12, 4000000); */
1644 * 103 0 0 C1 C2 C3 C4
1645 * C1 - CRC-32 of the entire source TrueType font
1646 * C2 - number of glyphs in the subset
1647 * C3 - CRC-32 of the glyph array
1648 * C4 - CRC-32 of the encoding array
1650 * All CRC-32 numbers are presented as hexadecimal numbers
1653 snprintf(buf
, bufmax
, h17
, rtl_crc32(0, ttf
->ptr
, ttf
->fsize
), nGlyphs
, rtl_crc32(0, glyphArray
, nGlyphs
* 2), rtl_crc32(0, encoding
, nGlyphs
));
1654 outf
->WriteOString(buf
);
1655 snprintf(buf
, bufmax
, "%s", h13
);
1656 outf
->WriteOString(buf
);
1657 snprintf(buf
, bufmax
, h14
, XUnits(UPEm
, GetInt16(table
, 36)), XUnits(UPEm
, GetInt16(table
, 38)), XUnits(UPEm
, GetInt16(table
, 40)), XUnits(UPEm
, GetInt16(table
, 42)));
1658 outf
->WriteOString(buf
);
1659 snprintf(buf
, bufmax
, "%s", h15
);
1660 outf
->WriteOString(buf
);
1662 for (i
= 0; i
< nGlyphs
; i
++) {
1663 snprintf(buf
, bufmax
, h16
, encoding
[i
], i
);
1664 outf
->WriteOString(buf
);
1667 snprintf(buf
, bufmax
, h30
, nGlyphs
+1);
1668 outf
->WriteOString(buf
);
1669 snprintf(buf
, bufmax
, "%s", h31
);
1670 outf
->WriteOString(buf
);
1671 snprintf(buf
, bufmax
, "%s", h32
);
1672 outf
->WriteOString(buf
);
1674 for (i
= 0; i
< nGlyphs
; i
++) {
1675 snprintf(buf
, bufmax
, h33
, i
);
1676 outf
->WriteOString(buf
);
1677 int r
= GetTTGlyphOutline(ttf
, glyphArray
[i
] < ttf
->glyphCount() ? glyphArray
[i
] : 0, pa
, &metrics
, nullptr);
1680 n
= BSplineToPSPath(pa
.data(), r
, path
);
1682 n
= 0; /* glyph might have zero contours but valid metrics ??? */
1684 if (r
< 0) { /* glyph is not present in the font - pa array was not allocated, so no need to free it */
1688 snprintf(buf
, bufmax
, "\t%d %d %d %d %d %d setcachedevice\n",
1689 wmode
== 0 ? XUnits(UPEm
, metrics
.aw
) : 0,
1690 wmode
== 0 ? 0 : -XUnits(UPEm
, metrics
.ah
),
1691 XUnits(UPEm
, metrics
.xMin
),
1692 XUnits(UPEm
, metrics
.yMin
),
1693 XUnits(UPEm
, metrics
.xMax
),
1694 XUnits(UPEm
, metrics
.yMax
));
1695 outf
->WriteOString(buf
);
1697 for (j
= 0; j
< n
; j
++)
1699 switch (path
[j
].type
)
1702 snprintf(buf
, bufmax
, "\t%d %d moveto\n", XUnits(UPEm
, path
[j
].x1
), XUnits(UPEm
, path
[j
].y1
));
1703 outf
->WriteOString(buf
);
1707 snprintf(buf
, bufmax
, "\t%d %d lineto\n", XUnits(UPEm
, path
[j
].x1
), XUnits(UPEm
, path
[j
].y1
));
1708 outf
->WriteOString(buf
);
1712 snprintf(buf
, bufmax
, "\t%d %d %d %d %d %d curveto\n", XUnits(UPEm
, path
[j
].x1
), XUnits(UPEm
, path
[j
].y1
), XUnits(UPEm
, path
[j
].x2
), XUnits(UPEm
, path
[j
].y2
), XUnits(UPEm
, path
[j
].x3
), XUnits(UPEm
, path
[j
].y3
));
1713 outf
->WriteOString(buf
);
1717 snprintf(buf
, bufmax
, "\tclosepath\n");
1718 outf
->WriteOString(buf
);
1726 snprintf(buf
, bufmax
, "\tfill\n"); /* if glyph is not a whitespace character */
1727 outf
->WriteOString(buf
);
1730 snprintf(buf
, bufmax
, "%s", h34
);
1731 outf
->WriteOString(buf
);
1735 snprintf(buf
, bufmax
, "%s", h35
);
1736 outf
->WriteOString(buf
);
1738 snprintf(buf
, bufmax
, "%s", h40
);
1739 outf
->WriteOString(buf
);
1740 snprintf(buf
, bufmax
, h41
, fname
);
1741 outf
->WriteOString(buf
);
1743 return SFErrCodes::Ok
;
1746 SFErrCodes
CreateTTFromTTGlyphs(AbstractTrueTypeFont
*ttf
,
1747 std::vector
<sal_uInt8
>& rOutBuffer
,
1748 sal_uInt16
const *glyphArray
,
1749 sal_uInt8
const *encoding
,
1752 std::unique_ptr
<TrueTypeTableGeneric
> cvt
, prep
, fpgm
, os2
;
1753 std::unique_ptr
<TrueTypeTableName
> name
;
1754 std::unique_ptr
<TrueTypeTableMaxp
> maxp
;
1755 std::unique_ptr
<TrueTypeTableHhea
> hhea
;
1756 std::unique_ptr
<TrueTypeTableHead
> head
;
1757 std::unique_ptr
<TrueTypeTableGlyf
> glyf
;
1758 std::unique_ptr
<TrueTypeTableCmap
> cmap
;
1759 std::unique_ptr
<TrueTypeTablePost
> post
;
1763 TrueTypeCreator
ttcr(T_true
);
1767 std::vector
<NameRecord
> names
;
1768 GetTTNameRecords(ttf
, names
);
1769 name
.reset(new TrueTypeTableName(std::move(names
)));
1772 sal_uInt32 nTableSize
;
1773 const sal_uInt8
* p
= ttf
->table(O_maxp
, nTableSize
);
1774 maxp
.reset(new TrueTypeTableMaxp(p
, nTableSize
));
1777 p
= ttf
->table(O_hhea
, nTableSize
);
1778 if (p
&& nTableSize
>= HHEA_caretSlopeRun_offset
+ 2)
1779 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
)));
1781 hhea
.reset(new TrueTypeTableHhea(0, 0, 0, 0, 0));
1785 p
= ttf
->table(O_head
, nTableSize
);
1786 assert(p
!= nullptr);
1787 head
.reset(new TrueTypeTableHead(GetInt32(p
, HEAD_fontRevision_offset
),
1788 GetUInt16(p
, HEAD_flags_offset
),
1789 GetUInt16(p
, HEAD_unitsPerEm_offset
),
1790 p
+HEAD_created_offset
,
1791 GetUInt16(p
, HEAD_macStyle_offset
),
1792 GetUInt16(p
, HEAD_lowestRecPPEM_offset
),
1793 GetInt16(p
, HEAD_fontDirectionHint_offset
)));
1797 glyf
.reset(new TrueTypeTableGlyf());
1798 std::unique_ptr
<sal_uInt32
[]> gID(new sal_uInt32
[nGlyphs
]);
1800 for (i
= 0; i
< nGlyphs
; i
++) {
1801 gID
[i
] = glyf
->glyfAdd(GetTTRawGlyphData(ttf
, glyphArray
[i
]), ttf
);
1805 cmap
.reset(new TrueTypeTableCmap());
1807 for (i
=0; i
< nGlyphs
; i
++) {
1808 cmap
->cmapAdd(0x010000, encoding
[i
], gID
[i
]);
1812 if ((p
= ttf
->table(O_cvt
, nTableSize
)) != nullptr)
1813 cvt
.reset(new TrueTypeTableGeneric(T_cvt
, nTableSize
, p
));
1816 if ((p
= ttf
->table(O_prep
, nTableSize
)) != nullptr)
1817 prep
.reset(new TrueTypeTableGeneric(T_prep
, nTableSize
, p
));
1820 if ((p
= ttf
->table(O_fpgm
, nTableSize
)) != nullptr)
1821 fpgm
.reset(new TrueTypeTableGeneric(T_fpgm
, nTableSize
, p
));
1824 if ((p
= ttf
->table(O_post
, nTableSize
)) != nullptr)
1826 sal_Int32 nItalic
= (POST_italicAngle_offset
+ 4 < nTableSize
) ?
1827 GetInt32(p
, POST_italicAngle_offset
) : 0;
1828 sal_Int16 nPosition
= (POST_underlinePosition_offset
+ 2 < nTableSize
) ?
1829 GetInt16(p
, POST_underlinePosition_offset
) : 0;
1830 sal_Int16 nThickness
= (POST_underlineThickness_offset
+ 2 < nTableSize
) ?
1831 GetInt16(p
, POST_underlineThickness_offset
) : 0;
1832 sal_uInt32 nFixedPitch
= (POST_isFixedPitch_offset
+ 4 < nTableSize
) ?
1833 GetUInt32(p
, POST_isFixedPitch_offset
) : 0;
1835 post
.reset(new TrueTypeTablePost(0x00030000,
1837 nThickness
, nFixedPitch
));
1840 post
.reset(new TrueTypeTablePost(0x00030000, 0, 0, 0, 0));
1842 ttcr
.AddTable(std::move(name
)); ttcr
.AddTable(std::move(maxp
)); ttcr
.AddTable(std::move(hhea
));
1843 ttcr
.AddTable(std::move(head
)); ttcr
.AddTable(std::move(glyf
)); ttcr
.AddTable(std::move(cmap
));
1844 ttcr
.AddTable(std::move(cvt
)); ttcr
.AddTable(std::move(prep
)); ttcr
.AddTable(std::move(fpgm
));
1845 ttcr
.AddTable(std::move(post
)); ttcr
.AddTable(std::move(os2
));
1847 res
= ttcr
.StreamToMemory(rOutBuffer
);
1848 #if OSL_DEBUG_LEVEL > 1
1849 SAL_WARN_IF(res
!= SFErrCodes::Ok
, "vcl.fonts", "StreamToMemory: error code: "
1850 << (int) res
<< ".");
1858 void FillFontSubsetInfo(AbstractTrueTypeFont
* ttf
, FontSubsetInfo
& rInfo
)
1860 TTGlobalFontInfo aTTInfo
;
1861 GetTTGlobalFontInfo(ttf
, &aTTInfo
);
1863 rInfo
.m_aPSName
= OUString::fromUtf8(aTTInfo
.psname
);
1864 rInfo
.m_nFontType
= FontType::SFNT_TTF
;
1866 = tools::Rectangle(Point(aTTInfo
.xMin
, aTTInfo
.yMin
), Point(aTTInfo
.xMax
, aTTInfo
.yMax
));
1867 rInfo
.m_nCapHeight
= aTTInfo
.yMax
; // Well ...
1868 rInfo
.m_nAscent
= aTTInfo
.winAscent
;
1869 rInfo
.m_nDescent
= aTTInfo
.winDescent
;
1871 // mac fonts usually do not have an OS2-table
1872 // => get valid ascent/descent values from other tables
1873 if (!rInfo
.m_nAscent
)
1874 rInfo
.m_nAscent
= +aTTInfo
.typoAscender
;
1875 if (!rInfo
.m_nAscent
)
1876 rInfo
.m_nAscent
= +aTTInfo
.ascender
;
1877 if (!rInfo
.m_nDescent
)
1878 rInfo
.m_nDescent
= +aTTInfo
.typoDescender
;
1879 if (!rInfo
.m_nDescent
)
1880 rInfo
.m_nDescent
= -aTTInfo
.descender
;
1882 rInfo
.m_bFilled
= true;
1885 bool CreateCFFfontSubset(const unsigned char* pFontBytes
, int nByteLength
,
1886 std::vector
<sal_uInt8
>& rOutBuffer
, const sal_GlyphId
* pGlyphIds
,
1887 const sal_uInt8
* pEncoding
, int nGlyphCount
, FontSubsetInfo
& rInfo
)
1889 utl::TempFileFast aTempFile
;
1890 SvStream
* pStream
= aTempFile
.GetStream(StreamMode::READWRITE
);
1892 rInfo
.LoadFont(FontType::CFF_FONT
, pFontBytes
, nByteLength
);
1893 bool bRet
= rInfo
.CreateFontSubset(FontType::TYPE1_PFB
, pStream
, nullptr, pGlyphIds
, pEncoding
,
1898 rOutBuffer
.resize(pStream
->TellEnd());
1900 auto nRead
= pStream
->ReadBytes(rOutBuffer
.data(), rOutBuffer
.size());
1901 if (nRead
!= rOutBuffer
.size())
1912 bool CreateTTFfontSubset(vcl::AbstractTrueTypeFont
& rTTF
, std::vector
<sal_uInt8
>& rOutBuffer
,
1913 const sal_GlyphId
* pGlyphIds
, const sal_uInt8
* pEncoding
,
1914 const int nOrigGlyphCount
, FontSubsetInfo
& rInfo
)
1916 // Get details about the subset font.
1917 FillFontSubsetInfo(&rTTF
, rInfo
);
1919 // Shortcut for CFF-subsetting.
1921 const sal_uInt8
* pCFF
= rTTF
.table(O_CFF
, nCFF
);
1923 return CreateCFFfontSubset(pCFF
, nCFF
, rOutBuffer
, pGlyphIds
, pEncoding
,
1924 nOrigGlyphCount
, rInfo
);
1926 // Multiple questions:
1927 // - Why is there a glyph limit?
1928 // MacOS used to handle 257 glyphs...
1929 // Also the much more complex PrintFontManager variant has this limit.
1930 // Also the very first implementation has the limit in
1931 // commit 8789ed701e98031f2a1657ea0dfd6f7a0b050992
1932 // - Why doesn't the PrintFontManager care about the fake glyph? It
1933 // is used on all unx platforms to create the subset font.
1934 // - Should the SAL_WARN actually be asserts, like on MacOS?
1935 if (nOrigGlyphCount
> 256)
1937 SAL_WARN("vcl.fonts", "too many glyphs for subsetting");
1941 int nGlyphCount
= nOrigGlyphCount
;
1942 sal_uInt16 aShortIDs
[256];
1943 sal_uInt8 aTempEncs
[256];
1945 // handle the undefined / first font glyph
1946 int nNotDef
= -1, i
;
1947 for (i
= 0; i
< nGlyphCount
; ++i
)
1949 aTempEncs
[i
] = pEncoding
[i
];
1950 aShortIDs
[i
] = static_cast<sal_uInt16
>(pGlyphIds
[i
]);
1956 // nNotDef glyph must be in pos 0 => swap glyphids
1961 if (nGlyphCount
== 256)
1963 SAL_WARN("vcl.fonts", "too many glyphs for subsetting");
1966 nNotDef
= nGlyphCount
++;
1969 aShortIDs
[nNotDef
] = aShortIDs
[0];
1970 aTempEncs
[nNotDef
] = aTempEncs
[0];
1975 // write subset into destination file
1976 return (CreateTTFromTTGlyphs(&rTTF
, rOutBuffer
, aShortIDs
, aTempEncs
, nGlyphCount
)
1977 == vcl::SFErrCodes::Ok
);
1980 GlyphOffsets::GlyphOffsets(sal_uInt8
*sfntP
, sal_uInt32 sfntLen
)
1982 sal_uInt8
*loca
= nullptr;
1983 sal_uInt16 numTables
= GetUInt16(sfntP
, 4);
1984 sal_uInt32 locaLen
= 0;
1985 sal_Int16 indexToLocFormat
= 0;
1987 sal_uInt32 nMaxPossibleTables
= sfntLen
/ (3*sizeof(sal_uInt32
)); /*the three GetUInt32 calls*/
1988 if (numTables
> nMaxPossibleTables
)
1990 SAL_WARN( "vcl.fonts", "GlyphOffsetsNew claimed to have "
1991 << numTables
<< " tables, but that's impossibly large");
1992 numTables
= nMaxPossibleTables
;
1995 for (sal_uInt16 i
= 0; i
< numTables
; i
++) {
1996 sal_uInt32 nLargestFixedOffsetPos
= 12 + 16 * i
+ 12;
1997 sal_uInt32 nMinSize
= nLargestFixedOffsetPos
+ sizeof(sal_uInt32
);
1998 if (nMinSize
> sfntLen
)
2000 SAL_WARN( "vcl.fonts", "GlyphOffsetsNew claimed to have "
2001 << numTables
<< " tables, but only space for " << i
);
2005 sal_uInt32 tag
= GetUInt32(sfntP
, 12 + 16 * i
);
2006 sal_uInt32 off
= GetUInt32(sfntP
, 12 + 16 * i
+ 8);
2007 sal_uInt32 len
= GetUInt32(sfntP
, nLargestFixedOffsetPos
);
2009 if (tag
== T_loca
) {
2012 } else if (tag
== T_head
) {
2013 indexToLocFormat
= GetInt16(sfntP
+ off
, 50);
2017 this->nGlyphs
= locaLen
/ ((indexToLocFormat
== 1) ? 4 : 2);
2018 assert(this->nGlyphs
!= 0);
2019 this->offs
= std::make_unique
<sal_uInt32
[]>(this->nGlyphs
);
2021 for (sal_uInt32 i
= 0; i
< this->nGlyphs
; i
++) {
2022 if (indexToLocFormat
== 1) {
2023 this->offs
[i
] = GetUInt32(loca
, i
* 4);
2025 this->offs
[i
] = GetUInt16(loca
, i
* 2) << 1;
2030 static void DumpSfnts(SvStream
*outf
, sal_uInt8
*sfntP
, sal_uInt32 sfntLen
)
2034 SAL_WARN( "vcl.fonts", "DumpSfnts sfntLen is too short: "
2035 << sfntLen
<< " legal min is: " << 12);
2039 const sal_uInt32 nSpaceForTables
= sfntLen
- 12;
2040 const sal_uInt32 nTableSize
= 16;
2041 const sal_uInt32 nMaxPossibleTables
= nSpaceForTables
/nTableSize
;
2044 sal_uInt16 i
, numTables
= GetUInt16(sfntP
, 4);
2045 GlyphOffsets
go(sfntP
, sfntLen
);
2046 sal_uInt8
const pad
[] = {0,0,0,0}; /* zeroes */
2048 if (numTables
> nMaxPossibleTables
)
2050 SAL_WARN( "vcl.fonts", "DumpSfnts claimed to have "
2051 << numTables
<< " tables, but only space for " << nMaxPossibleTables
);
2052 numTables
= nMaxPossibleTables
;
2055 assert(numTables
<= 9); /* Type42 has 9 required tables */
2057 std::unique_ptr
<sal_uInt32
[]> offs(new sal_uInt32
[numTables
]);
2059 outf
->WriteOString("/sfnts [");
2061 h
.BlockWrite(sfntP
, 12); /* stream out the Offset Table */
2062 h
.BlockWrite(sfntP
+12, 16 * numTables
); /* stream out the Table Directory */
2064 for (i
=0; i
<numTables
; i
++)
2066 sal_uInt32 nLargestFixedOffsetPos
= 12 + 16 * i
+ 12;
2067 sal_uInt32 nMinSize
= nLargestFixedOffsetPos
+ sizeof(sal_uInt32
);
2068 if (nMinSize
> sfntLen
)
2070 SAL_WARN( "vcl.fonts", "DumpSfnts claimed to have "
2071 << numTables
<< " tables, but only space for " << i
);
2075 sal_uInt32 tag
= GetUInt32(sfntP
, 12 + 16 * i
);
2076 sal_uInt32 off
= GetUInt32(sfntP
, 12 + 16 * i
+ 8);
2079 SAL_WARN( "vcl.fonts", "DumpSfnts claims offset of "
2080 << off
<< " but max possible is " << sfntLen
);
2083 sal_uInt8
*pRecordStart
= sfntP
+ off
;
2084 sal_uInt32 len
= GetUInt32(sfntP
, nLargestFixedOffsetPos
);
2085 sal_uInt32 nMaxLenPossible
= sfntLen
- off
;
2086 if (len
> nMaxLenPossible
)
2088 SAL_WARN( "vcl.fonts", "DumpSfnts claims len of "
2089 << len
<< " but only space for " << nMaxLenPossible
);
2095 h
.BlockWrite(pRecordStart
, len
);
2099 sal_uInt8
*glyf
= pRecordStart
;
2100 sal_uInt8
*eof
= pRecordStart
+ nMaxLenPossible
;
2101 for (sal_uInt32 j
= 0; j
< go
.nGlyphs
- 1; j
++)
2103 sal_uInt32 nStartOffset
= go
.offs
[j
];
2104 sal_uInt8
*pSubRecordStart
= glyf
+ nStartOffset
;
2105 if (pSubRecordStart
> eof
)
2107 SAL_WARN( "vcl.fonts", "DumpSfnts start glyf claims offset of "
2108 << pSubRecordStart
- sfntP
<< " but max possible is " << eof
- sfntP
);
2112 sal_uInt32 nEndOffset
= go
.offs
[j
+ 1];
2113 sal_uInt8
*pSubRecordEnd
= glyf
+ nEndOffset
;
2114 if (pSubRecordEnd
> eof
)
2116 SAL_WARN( "vcl.fonts", "DumpSfnts end glyf offset of "
2117 << pSubRecordEnd
- sfntP
<< " but max possible is " << eof
- sfntP
);
2121 sal_uInt32 l
= pSubRecordEnd
- pSubRecordStart
;
2122 h
.BlockWrite(pSubRecordStart
, l
);
2125 h
.BlockWrite(pad
, (4 - (len
& 3)) & 3);
2128 outf
->WriteOString("] def\n");
2131 SFErrCodes
CreateT42FromTTGlyphs(TrueTypeFont
*ttf
,
2134 sal_uInt16
const *glyphArray
,
2135 sal_uInt8
*encoding
,
2138 std::unique_ptr
<TrueTypeTable
> head
, hhea
, maxp
, cvt
, prep
, fpgm
;
2139 std::unique_ptr
<TrueTypeTableGlyf
> glyf
;
2146 int UPEm
= ttf
->unitsPerEm();
2148 if (nGlyphs
>= 256) return SFErrCodes::GlyphNum
;
2150 assert(psname
!= nullptr);
2152 TrueTypeCreator
ttcr(T_true
);
2155 sal_uInt32 nTableSize
;
2156 const sal_uInt8
* p
= ttf
->table(O_head
, nTableSize
);
2157 const sal_uInt8
* headP
= p
;
2158 assert(p
!= nullptr);
2159 head
.reset(new TrueTypeTableHead(GetInt32(p
, HEAD_fontRevision_offset
), GetUInt16(p
, HEAD_flags_offset
), GetUInt16(p
, HEAD_unitsPerEm_offset
), p
+HEAD_created_offset
, GetUInt16(p
, HEAD_macStyle_offset
), GetUInt16(p
, HEAD_lowestRecPPEM_offset
), GetInt16(p
, HEAD_fontDirectionHint_offset
)));
2160 ver
= GetUInt16(p
, HEAD_majorVersion_offset
);
2161 rev
= GetInt32(p
, HEAD_fontRevision_offset
);
2164 p
= ttf
->table(O_hhea
, nTableSize
);
2166 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
)));
2168 hhea
.reset(new TrueTypeTableHhea(0, 0, 0, 0, 0));
2171 p
= ttf
->table(O_maxp
, nTableSize
);
2172 maxp
.reset(new TrueTypeTableMaxp(p
, nTableSize
));
2175 if ((p
= ttf
->table(O_cvt
, nTableSize
)) != nullptr)
2176 cvt
.reset(new TrueTypeTableGeneric(T_cvt
, nTableSize
, p
));
2179 if ((p
= ttf
->table(O_prep
, nTableSize
)) != nullptr)
2180 prep
.reset(new TrueTypeTableGeneric(T_prep
, nTableSize
, p
));
2183 if ((p
= ttf
->table(O_fpgm
, nTableSize
)) != nullptr)
2184 fpgm
.reset(new TrueTypeTableGeneric(T_fpgm
, nTableSize
, p
));
2187 glyf
.reset(new TrueTypeTableGlyf());
2188 std::unique_ptr
<sal_uInt16
[]> gID(new sal_uInt16
[nGlyphs
]);
2190 for (i
= 0; i
< nGlyphs
; i
++) {
2191 gID
[i
] = static_cast<sal_uInt16
>(glyf
->glyfAdd(GetTTRawGlyphData(ttf
, glyphArray
[i
]), ttf
));
2194 const int nGlyfCount
= static_cast<int>(glyf
->glyfCount());
2196 ttcr
.AddTable(std::move(head
)); ttcr
.AddTable(std::move(hhea
)); ttcr
.AddTable(std::move(maxp
)); ttcr
.AddTable(std::move(cvt
));
2197 ttcr
.AddTable(std::move(prep
)); ttcr
.AddTable(std::move(glyf
)); ttcr
.AddTable(std::move(fpgm
));
2199 std::vector
<sal_uInt8
> aOutBuffer
;
2200 if ((res
= ttcr
.StreamToMemory(aOutBuffer
)) != SFErrCodes::Ok
) {
2204 constexpr int bufmax
= 256;
2207 snprintf(buf
, bufmax
, "%%!PS-TrueTypeFont-%d.%d-%d.%d\n", static_cast<int>(ver
), static_cast<int>(ver
& 0xFF), static_cast<int>(rev
>>16), static_cast<int>(rev
& 0xFFFF));
2208 outf
->WriteOString(buf
);
2209 snprintf(buf
, bufmax
, "%%%%Creator: %s %s %s\n", modname
, modver
, modextra
);
2210 outf
->WriteOString(buf
);
2211 snprintf(buf
, bufmax
, "%%- Font subset generated from a source font file: '%s'\n", ttf
->fileName().data());
2212 outf
->WriteOString(buf
);
2213 snprintf(buf
, bufmax
, "%%- Original font name: %s\n", ttf
->psname
.getStr());
2214 outf
->WriteOString(buf
);
2215 snprintf(buf
, bufmax
, "%%- Original font family: %s\n", ttf
->family
.getStr());
2216 outf
->WriteOString(buf
);
2217 snprintf(buf
, bufmax
, "%%- Original font sub-family: %s\n", ttf
->subfamily
.getStr());
2218 outf
->WriteOString(buf
);
2219 snprintf(buf
, bufmax
, "11 dict begin\n");
2220 outf
->WriteOString(buf
);
2221 snprintf(buf
, bufmax
, "/FontName (%s) cvn def\n", psname
);
2222 outf
->WriteOString(buf
);
2223 snprintf(buf
, bufmax
, "/PaintType 0 def\n");
2224 outf
->WriteOString(buf
);
2225 snprintf(buf
, bufmax
, "/FontMatrix [1 0 0 1 0 0] def\n");
2226 outf
->WriteOString(buf
);
2227 snprintf(buf
, bufmax
, "/FontBBox [%d %d %d %d] def\n", XUnits(UPEm
, GetInt16(headP
, HEAD_xMin_offset
)), XUnits(UPEm
, GetInt16(headP
, HEAD_yMin_offset
)), XUnits(UPEm
, GetInt16(headP
, HEAD_xMax_offset
)), XUnits(UPEm
, GetInt16(headP
, HEAD_yMax_offset
)));
2228 outf
->WriteOString(buf
);
2229 snprintf(buf
, bufmax
, "/FontType 42 def\n");
2230 outf
->WriteOString(buf
);
2231 snprintf(buf
, bufmax
, "/Encoding 256 array def\n");
2232 outf
->WriteOString(buf
);
2233 snprintf(buf
, bufmax
, " 0 1 255 {Encoding exch /.notdef put} for\n");
2234 outf
->WriteOString(buf
);
2236 for (i
= 1; i
<nGlyphs
; i
++) {
2237 snprintf(buf
, bufmax
, "Encoding %d /glyph%u put\n", encoding
[i
], gID
[i
]);
2238 outf
->WriteOString(buf
);
2240 snprintf(buf
, bufmax
, "/XUID [103 0 1 16#%08X %u 16#%08X 16#%08X] def\n", static_cast<unsigned int>(rtl_crc32(0, ttf
->ptr
, ttf
->fsize
)), static_cast<unsigned int>(nGlyphs
), static_cast<unsigned int>(rtl_crc32(0, glyphArray
, nGlyphs
* 2)), static_cast<unsigned int>(rtl_crc32(0, encoding
, nGlyphs
)));
2241 outf
->WriteOString(buf
);
2243 DumpSfnts(outf
, aOutBuffer
.data(), aOutBuffer
.size());
2245 /* dump charstrings */
2246 snprintf(buf
, bufmax
, "/CharStrings %d dict dup begin\n", nGlyphs
);
2247 outf
->WriteOString(buf
);
2248 snprintf(buf
, bufmax
, "/.notdef 0 def\n");
2249 outf
->WriteOString(buf
);
2250 for (i
= 1; i
< nGlyfCount
; i
++) {
2251 snprintf(buf
, bufmax
, "/glyph%d %d def\n", i
, i
);
2252 outf
->WriteOString(buf
);
2254 snprintf(buf
, bufmax
, "end readonly def\n");
2255 outf
->WriteOString(buf
);
2257 snprintf(buf
, bufmax
, "FontName currentdict end definefont pop\n");
2258 outf
->WriteOString(buf
);
2260 return SFErrCodes::Ok
;
2263 bool GetTTGlobalFontHeadInfo(const AbstractTrueTypeFont
*ttf
, int& xMin
, int& yMin
, int& xMax
, int& yMax
, sal_uInt16
& macStyle
)
2265 sal_uInt32 table_size
;
2266 const sal_uInt8
* table
= ttf
->table(O_head
, table_size
);
2267 if (table_size
< 46)
2270 const int UPEm
= ttf
->unitsPerEm();
2273 xMin
= XUnits(UPEm
, GetInt16(table
, HEAD_xMin_offset
));
2274 yMin
= XUnits(UPEm
, GetInt16(table
, HEAD_yMin_offset
));
2275 xMax
= XUnits(UPEm
, GetInt16(table
, HEAD_xMax_offset
));
2276 yMax
= XUnits(UPEm
, GetInt16(table
, HEAD_yMax_offset
));
2277 macStyle
= GetUInt16(table
, HEAD_macStyle_offset
);
2281 void GetTTGlobalFontInfo(AbstractTrueTypeFont
*ttf
, TTGlobalFontInfo
*info
)
2283 int UPEm
= ttf
->unitsPerEm();
2285 info
->family
= ttf
->family
;
2286 info
->ufamily
= ttf
->ufamily
;
2287 info
->subfamily
= ttf
->subfamily
;
2288 info
->usubfamily
= ttf
->usubfamily
;
2289 info
->psname
= ttf
->psname
;
2290 info
->microsoftSymbolEncoded
= ttf
->IsMicrosoftSymbolEncoded();
2292 sal_uInt32 table_size
;
2293 const sal_uInt8
* table
= ttf
->table(O_OS2
, table_size
);
2294 if (table_size
>= 42)
2296 info
->weight
= GetUInt16(table
, OS2_usWeightClass_offset
);
2297 info
->width
= GetUInt16(table
, OS2_usWidthClass_offset
);
2299 if (table_size
>= OS2_V0_length
&& UPEm
!= 0) {
2300 info
->typoAscender
= XUnits(UPEm
,GetInt16(table
, OS2_typoAscender_offset
));
2301 info
->typoDescender
= XUnits(UPEm
, GetInt16(table
, OS2_typoDescender_offset
));
2302 info
->typoLineGap
= XUnits(UPEm
, GetInt16(table
, OS2_typoLineGap_offset
));
2303 info
->winAscent
= XUnits(UPEm
, GetUInt16(table
, OS2_winAscent_offset
));
2304 info
->winDescent
= XUnits(UPEm
, GetUInt16(table
, OS2_winDescent_offset
));
2305 /* sanity check; some fonts treat winDescent as signed
2306 * violating the standard */
2307 if( info
->winDescent
> 5*UPEm
)
2308 info
->winDescent
= XUnits(UPEm
, GetInt16(table
, OS2_winDescent_offset
));
2310 memcpy(info
->panose
, table
+ OS2_panose_offset
, OS2_panoseNbBytes_offset
);
2311 info
->typeFlags
= GetUInt16( table
, OS2_fsType_offset
);
2314 table
= ttf
->table(O_post
, table_size
);
2315 if (table_size
>= 12 + sizeof(sal_uInt32
))
2317 info
->pitch
= GetUInt32(table
, POST_isFixedPitch_offset
);
2318 info
->italicAngle
= GetInt32(table
, POST_italicAngle_offset
);
2321 GetTTGlobalFontHeadInfo(ttf
, info
->xMin
, info
->yMin
, info
->xMax
, info
->yMax
, info
->macStyle
);
2323 table
= ttf
->table(O_hhea
, table_size
);
2324 if (table_size
>= 10 && UPEm
!= 0)
2326 info
->ascender
= XUnits(UPEm
, GetInt16(table
, HHEA_ascender_offset
));
2327 info
->descender
= XUnits(UPEm
, GetInt16(table
, HHEA_descender_offset
));
2328 info
->linegap
= XUnits(UPEm
, GetInt16(table
, HHEA_lineGap_offset
));
2332 std::unique_ptr
<GlyphData
> GetTTRawGlyphData(AbstractTrueTypeFont
*ttf
, sal_uInt32 glyphID
)
2334 if (glyphID
>= ttf
->glyphCount())
2337 sal_uInt32 hmtxlength
;
2338 const sal_uInt8
* hmtx
= ttf
->table(O_hmtx
, hmtxlength
);
2343 sal_uInt32 glyflength
;
2344 const sal_uInt8
* glyf
= ttf
->table(O_glyf
, glyflength
);
2347 /* #127161# check the glyph offsets */
2348 sal_uInt32 nNextOffset
= ttf
->glyphOffset(glyphID
+ 1);
2349 if (nNextOffset
> glyflength
)
2352 sal_uInt32 nOffset
= ttf
->glyphOffset(glyphID
);
2353 if (nOffset
> nNextOffset
)
2356 sal_uInt32 length
= nNextOffset
- nOffset
;
2358 std::unique_ptr
<GlyphData
> d(new GlyphData
);
2361 const sal_uInt8
* srcptr
= glyf
+ ttf
->glyphOffset(glyphID
);
2362 const size_t nChunkLen
= ((length
+ 1) & ~1);
2363 d
->ptr
.reset(new sal_uInt8
[nChunkLen
]);
2364 memcpy(d
->ptr
.get(), srcptr
, length
);
2365 memset(d
->ptr
.get() + length
, 0, nChunkLen
- length
);
2366 d
->compflag
= (GetInt16( srcptr
, 0 ) < 0);
2369 d
->compflag
= false;
2372 d
->glyphID
= glyphID
;
2373 d
->nbytes
= static_cast<sal_uInt16
>((length
+ 1) & ~1);
2375 /* now calculate npoints and ncontours */
2376 std::vector
<ControlPoint
> cp
;
2377 n
= GetTTGlyphPoints(ttf
, glyphID
, cp
);
2381 for (int i
= 0; i
< n
; i
++)
2383 if (cp
[i
].flags
& 0x8000)
2386 d
->npoints
= static_cast<sal_uInt16
>(n
);
2387 d
->ncontours
= static_cast<sal_uInt16
>(m
);
2393 /* get advance width and left sidebearing */
2394 sal_uInt32 nAwOffset
;
2395 sal_uInt32 nLsboffset
;
2396 if (glyphID
< ttf
->horzMetricCount()) {
2397 nAwOffset
= 4 * glyphID
;
2398 nLsboffset
= 4 * glyphID
+ 2;
2400 nAwOffset
= 4 * (ttf
->horzMetricCount() - 1);
2401 nLsboffset
= (ttf
->horzMetricCount() * 4) + ((glyphID
- ttf
->horzMetricCount()) * 2);
2404 if (nAwOffset
+ 2 <= hmtxlength
)
2405 d
->aw
= GetUInt16(hmtx
, nAwOffset
);
2408 SAL_WARN("vcl.fonts", "hmtx offset " << nAwOffset
<< " not available");
2411 if (nLsboffset
+ 2 <= hmtxlength
)
2412 d
->lsb
= GetInt16(hmtx
, nLsboffset
);
2415 SAL_WARN("vcl.fonts", "hmtx offset " << nLsboffset
<< " not available");
2422 void GetTTNameRecords(AbstractTrueTypeFont
const *ttf
, std::vector
<NameRecord
>& nr
)
2424 sal_uInt32 nTableSize
;
2425 const sal_uInt8
* table
= ttf
->table(O_name
, nTableSize
);
2431 #if OSL_DEBUG_LEVEL > 1
2432 SAL_WARN("vcl.fonts", "O_name table too small.");
2437 sal_uInt16 n
= GetUInt16(table
, 2);
2438 sal_uInt32 nStrBase
= GetUInt16(table
, 4);
2443 const sal_uInt32 remaining_table_size
= nTableSize
-6;
2444 const sal_uInt32 nMinRecordSize
= 12;
2445 const sal_uInt32 nMaxRecords
= remaining_table_size
/ nMinRecordSize
;
2446 if (n
> nMaxRecords
)
2448 SAL_WARN("vcl.fonts", "Parsing error in " << OUString::createFromAscii(ttf
->fileName()) <<
2449 ": " << nMaxRecords
<< " max possible entries, but " <<
2450 n
<< " claimed, truncating");
2456 for (i
= 0; i
< n
; i
++) {
2457 sal_uInt32 nLargestFixedOffsetPos
= 6 + 10 + 12 * i
;
2458 sal_uInt32 nMinSize
= nLargestFixedOffsetPos
+ sizeof(sal_uInt16
);
2459 if (nMinSize
> nTableSize
)
2461 SAL_WARN( "vcl.fonts", "Font " << OUString::createFromAscii(ttf
->fileName()) << " claimed to have "
2462 << n
<< " name records, but only space for " << i
);
2466 nr
[i
].platformID
= GetUInt16(table
, 6 + 0 + 12 * i
);
2467 nr
[i
].encodingID
= GetUInt16(table
, 6 + 2 + 12 * i
);
2468 nr
[i
].languageID
= LanguageType(GetUInt16(table
, 6 + 4 + 12 * i
));
2469 nr
[i
].nameID
= GetUInt16(table
, 6 + 6 + 12 * i
);
2470 sal_uInt16 slen
= GetUInt16(table
, 6 + 8 + 12 * i
);
2471 sal_uInt32 nStrOffset
= GetUInt16(table
, nLargestFixedOffsetPos
);
2473 if (nStrBase
+ nStrOffset
+ slen
>= nTableSize
)
2476 const sal_uInt32 rec_string
= nStrBase
+ nStrOffset
;
2477 const size_t available_space
= rec_string
> nTableSize
? 0 : (nTableSize
- rec_string
);
2478 if (slen
<= available_space
)
2480 nr
[i
].sptr
.resize(slen
);
2481 memcpy(nr
[i
].sptr
.data(), table
+ rec_string
, slen
);
2484 // some fonts have 3.0 names => fix them to 3.1
2485 if( (nr
[i
].platformID
== 3) && (nr
[i
].encodingID
== 0) )
2486 nr
[i
].encodingID
= 1;
2490 template<size_t N
> static void
2491 append(std::bitset
<N
> & rSet
, size_t const nOffset
, sal_uInt32
const nValue
)
2493 for (size_t i
= 0; i
< 32; ++i
)
2495 rSet
.set(nOffset
+ i
, (nValue
& (1U << i
)) != 0);
2500 std::optional
<std::bitset
<UnicodeCoverage::MAX_UC_ENUM
>> &rUnicodeRange
,
2501 std::optional
<std::bitset
<CodePageCoverage::MAX_CP_ENUM
>> &rCodePageRange
,
2502 const unsigned char* pTable
, size_t nLength
)
2505 // parse OS/2 header
2506 if (nLength
>= OS2_Legacy_length
)
2508 rUnicodeRange
= std::bitset
<UnicodeCoverage::MAX_UC_ENUM
>();
2509 append(*rUnicodeRange
, 0, GetUInt32(pTable
, OS2_ulUnicodeRange1_offset
));
2510 append(*rUnicodeRange
, 32, GetUInt32(pTable
, OS2_ulUnicodeRange2_offset
));
2511 append(*rUnicodeRange
, 64, GetUInt32(pTable
, OS2_ulUnicodeRange3_offset
));
2512 append(*rUnicodeRange
, 96, GetUInt32(pTable
, OS2_ulUnicodeRange4_offset
));
2514 if (nLength
>= OS2_V1_length
)
2516 rCodePageRange
= std::bitset
<CodePageCoverage::MAX_CP_ENUM
>();
2517 append(*rCodePageRange
, 0, GetUInt32(pTable
, OS2_ulCodePageRange1_offset
));
2518 append(*rCodePageRange
, 32, GetUInt32(pTable
, OS2_ulCodePageRange2_offset
));
2526 int TestFontSubset(const void* data
, sal_uInt32 size
)
2529 vcl::TrueTypeFont
* pTTF
= nullptr;
2530 if (OpenTTFontBuffer(data
, size
, 0, &pTTF
) == vcl::SFErrCodes::Ok
)
2532 vcl::TTGlobalFontInfo aInfo
;
2533 GetTTGlobalFontInfo(pTTF
, &aInfo
);
2535 sal_uInt16 aGlyphIds
[ 256 ] = {};
2536 sal_uInt8 aEncoding
[ 256 ] = {};
2538 for (sal_uInt16 c
= 32; c
< 256; ++c
)
2541 aGlyphIds
[c
] = c
- 31;
2544 std::vector
<sal_uInt8
> aBuffer
;
2545 CreateTTFromTTGlyphs(pTTF
, aBuffer
, aGlyphIds
, aEncoding
, 256);
2549 CloseTTFont( pTTF
);
2557 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */