Subversion Repositories Scribus

Compare Revisions

Ignore whitespace Rev 20102 → Rev 20103

/trunk/Scribus/CMakeLists.txt
475,12 → 475,12
SET(ENV{LD_PREBIND} 1 )
SET(ENV{LD_PREBIND_ALLOW_OVERLAP} 1 )
IF (WANT_DEBUG)
SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3 -fno-inline-functions -Wall -mmacosx-version-min=${OSXMINVER}")
SET(CMAKE_C_FLAGS_DEBUG "-O0 -g3 -fno-inline-functions -Wall -mmacosx-version-min=${OSXMINVER}")
SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -fno-inline-functions -Wall -mmacosx-version-min=${OSXMINVER}")
SET(CMAKE_C_FLAGS_DEBUG "-O0 -g -fno-inline-functions -Wall -mmacosx-version-min=${OSXMINVER}")
ENDIF (WANT_DEBUG)
IF (WANT_RELEASEWITHDEBUG)
SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g3 -fno-inline-functions -Wall -mmacosx-version-min=${OSXMINVER}")
SET(CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g3 -fno-inline-functions -Wall -mmacosx-version-min=${OSXMINVER}")
SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g -fno-inline-functions -Wall -mmacosx-version-min=${OSXMINVER}")
SET(CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g -fno-inline-functions -Wall -mmacosx-version-min=${OSXMINVER}")
ENDIF (WANT_RELEASEWITHDEBUG)
IF (NOT WANT_DEBUG AND NOT WANT_RELEASEWITHDEBUG)
SET(CMAKE_CXX_FLAGS_RELEASE "-O2 -fno-inline-functions -Wall -mmacosx-version-min=${OSXMINVER}")
494,8 → 494,8
SET(CMAKE_C_FLAGS_DEBUG)
ELSE(${CMAKE_GENERATOR} MATCHES "^(Visual Studio|NMake).*")
# vanilla gcc
SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3 -Wall -fstrict-aliasing")
SET(CMAKE_C_FLAGS_DEBUG "-O0 -g3 -Wall -fstrict-aliasing")
SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -Wall -fstrict-aliasing")
SET(CMAKE_C_FLAGS_DEBUG "-O0 -g -Wall -fstrict-aliasing")
IF (_machine_x86 OR _machine_x86_64)
SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fPIC")
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fPIC")
509,7 → 509,7
ELSE(${CMAKE_GENERATOR} MATCHES "^(Visual Studio|NMake).*")
SET(CMAKE_CXX_FLAGS_RELEASE "-O2 -Wall -fPIC")
SET(CMAKE_C_FLAGS_RELEASE "-O2 -Wall -fPIC")
ADD_DEFINITIONS(-DNDEBUG)
# ADD_DEFINITIONS(-DNDEBUG)
ENDIF(${CMAKE_GENERATOR} MATCHES "^(Visual Studio|NMake).*")
 
IF(WIN32)
/trunk/Scribus/scribus/CMakeLists.txt
647,6 → 647,7
pdflib_core.cpp
pdfoptions.cpp
pdfoptionsio.cpp
pdfwriter.cpp
pluginmanager.cpp
pp_proxy.cpp
prefscontext.cpp
/trunk/Scribus/scribus/fontlistmodel.cpp
23,29 → 23,38
psFont = loadIcon("font_type1_16.png");
substFont = loadIcon("font_subst16.png");
m_font_values = m_fonts.values();
m_font_names = m_fonts.keys();
}
 
void FontListModel::setFonts(SCFonts f)
void FontListModel::setFonts(QList<QString> f)
{
beginResetModel();
m_fonts = f;
m_font_values = m_fonts.values();
m_font_names = f;
m_font_values.clear();
m_embedMethod.clear();
for (int i = 0; i < m_font_names.length(); ++i)
{
ScFace font = m_fonts[m_font_names[i]];
m_font_values.append(font);
m_embedMethod.append(font.subset()? SubsetFont : font.embedPs()? EmbedFont : DontEmbed);
}
endResetModel();
}
 
int FontListModel::rowCount(const QModelIndex&) const
{
return m_fonts.size();
return m_font_names.size();
}
 
int FontListModel::rowCount()
{
return m_fonts.size();
return m_font_names.size();
}
 
int FontListModel::columnCount(const QModelIndex&) const
{
return 12;
return 10;
}
 
QVariant FontListModel::headerData(int section,
55,41 → 64,48
if (orientation == Qt::Vertical)
return QVariant(); // no verticals
// TODO for tooltips etc.
if (role != Qt::DisplayRole)
return QVariant();
 
switch (section)
{
case FontListModel::FontName:
return tr("Font Name");
case FontListModel::FontUsable:
return tr("Use Font");
case FontListModel::FontFamily:
return tr("Family");
case FontListModel::FontStyle:
return tr("Style");
case FontListModel::FontVariant:
return tr("Variant");
case FontListModel::FontType:
return tr("Type");
case FontListModel::FontFormat:
return tr("Format");
case FontListModel::FontEmbed:
return tr("Embed in PostScript");
case FontListModel::FontSubset:
return tr("Subset");
case FontListModel::FontAccess:
return tr("Access");
case FontListModel::FontInDoc:
return tr("Used in Doc");
case FontListModel::FontFile:
return tr("Path to Font File");
default:
return "Never should be shown";
};
 
// dummy return
return QVariant();
bool isDisplayRole = (role == Qt::DisplayRole);
switch(role)
{
case Qt::DisplayRole:
case Qt::ToolTipRole:
switch (section)
{
case FontListModel::FontName:
return tr("Font Name");
case FontListModel::FontUsable:
return isDisplayRole? QString("✓") : tr("Use Font");
// case FontListModel::FontFamily:
// return tr("Family");
// case FontListModel::FontStyle:
// return tr("Style");
// case FontListModel::FontVariant:
// return tr("Variant");
case FontListModel::FontType:
return isDisplayRole? QString("a") : tr("Type");
case FontListModel::FontFormat:
return isDisplayRole? QString("b") : tr("Format");
case FontListModel::FontEmbed:
return isDisplayRole? QString("c") : tr("Embed in PostScript");
case FontListModel::FontSubset:
return isDisplayRole? QString("d") : tr("Subset");
case FontListModel::FontOutline:
return isDisplayRole? QString("e") : tr("Outline");
case FontListModel::FontAccess:
return isDisplayRole? QString("f") : tr("Access");
case FontListModel::FontInDoc:
return isDisplayRole? QString("g") : tr("Used in Doc");
case FontListModel::FontFile:
return tr("Path to Font File");
default:
return "Never should be shown";
};
default:
// dummy return
return QVariant();
}
}
 
QVariant FontListModel::data(const QModelIndex & index,
115,20 → 131,28
};
}
 
if (role == Qt::DisplayRole)
if (role == Qt::DisplayRole || role == Qt::ToolTipRole)
{
switch (index.column())
{
case FontListModel::FontName:
return font.scName();
case FontListModel::FontFamily:
return font.family();
case FontListModel::FontStyle:
return font.style();
case FontListModel::FontVariant:
return font.variant();
if (role == Qt::DisplayRole)
return font.scName();
return
tr("Family") + ":\t" + font.family() + "\n"
+ tr("Style") + ":\t" + font.style() + "\n"
+ tr("Variant") + ":\t" + font.variant();
// case FontListModel::FontFamily:
// return font.family();
// case FontListModel::FontStyle:
// return font.style();
// case FontListModel::FontVariant:
// return font.variant();
case FontListModel::FontType:
{
if (role == Qt::DisplayRole)
return QVariant();
switch (font.type())
{
case ScFace::TYPE0:
172,31 → 196,52
return QVariant();
}
case FontListModel::FontFile:
return font.fontFilePath();
return font.fontPath();
case FontListModel::SortIndex:
if (font.scName()[0] == QChar('.'))
return font.scName().mid(1).toLower();
return font.scName().toLower();
default:
if (role == Qt::ToolTipRole
&&
(index.column() == FontListModel::FontUsable
|| index.column() == FontListModel::FontEmbed
|| index.column() == FontListModel::FontSubset
|| index.column() == FontListModel::FontOutline
)
)
{
return tr("Click to change the value");
}
return QVariant();
};
}
 
if (role == Qt::ToolTipRole
&&
(index.column() == FontListModel::FontUsable
|| index.column() == FontListModel::FontEmbed
|| index.column() == FontListModel::FontSubset
)
)
{
return tr("Click to change the value");
}
 
if (role == Qt::CheckStateRole && index.column() == FontListModel::FontUsable)
return (font.usable() ? Qt::Checked : Qt::Unchecked);
return !isLive() || (font.usable() ? Qt::Checked : Qt::Unchecked);
if (role == Qt::CheckStateRole && index.column() == FontListModel::FontEmbed)
return (font.embedPs() ? Qt::Checked : Qt::Unchecked);
{
if (isLive())
return (font.embedPs() ? Qt::Checked : Qt::Unchecked);
else
return (m_embedMethod[index.row()] >= EmbedFont ? Qt::Checked : Qt::Unchecked);
}
if (role == Qt::CheckStateRole && index.column() == FontListModel::FontSubset)
return (font.subset() ? Qt::Checked : Qt::Unchecked);
{
if (isLive())
return (font.subset() ? Qt::Checked : Qt::Unchecked);
else
return (m_embedMethod[index.row()] >= SubsetFont ? Qt::Checked : Qt::Unchecked);
}
if (role == Qt::CheckStateRole && index.column() == FontListModel::FontOutline)
{
if (isLive())
return (font.outline() ? Qt::Checked : Qt::Unchecked);
else
return (m_embedMethod[index.row()] >= OutlineFont ? Qt::Checked : Qt::Unchecked);
}
 
return QVariant();
}
209,50 → 254,80
return QAbstractTableModel::flags(index);
if (index.column() == FontListModel::FontUsable
|| index.column() == FontListModel::FontEmbed
|| index.column() == FontListModel::FontSubset)
|| index.column() == FontListModel::FontOutline
|| index.column() == FontListModel::FontSubset)
return Qt::ItemIsUserCheckable | /*Qt::ItemIsEditable |*/ defaultFlags;
else
return defaultFlags;
}
 
bool FontListModel::setData(const QModelIndex & index,
bool FontListModel::setData(const QModelIndex & idx,
const QVariant & value,
int role)
{
if (!index.isValid() || role != Qt::CheckStateRole)
if (!idx.isValid() || role != Qt::CheckStateRole)
{
qDebug("FontListModel::setData() out of Qt::CheckStateRole role");
return false;
}
 
/* ScFace f = m_fonts[m_fonts.keys().at(index.row())];
/* ScFace f = m_fonts[m_fonts.keys().at(idx.row())];
 
if (index.column() == FontListModel::FontUsable)
m_fonts[m_fonts.keys().at(index.row())].usable(!f.usable());
else if (index.column() == FontListModel::FontEmbed)
m_fonts[m_fonts.keys().at(index.row())].embedPs(!f.embedPs());
else if (index.column() == FontListModel::FontSubset)
m_fonts[m_fonts.keys().at(index.row())].subset(!f.subset());
if (idx.column() == FontListModel::FontUsable)
m_fonts[m_fonts.keys().at(idx.row())].usable(!f.usable());
else if (idx.column() == FontListModel::FontEmbed)
m_fonts[m_fonts.keys().at(idx.row())].embedPs(!f.embedPs());
else if (idx.column() == FontListModel::FontSubset)
m_fonts[m_fonts.keys().at(idx.row())].subset(!f.subset());
else
qDebug("FontListModel::setData() out of defined editable columns"); */
 
ScFace f = m_font_values[index.row()];
ScFace f = m_font_values[idx.row()];
 
if (index.column() == FontListModel::FontUsable)
f.usable(!f.usable());
else if (index.column() == FontListModel::FontEmbed)
f.embedPs(!f.embedPs());
else if (index.column() == FontListModel::FontSubset)
f.subset(!f.subset());
else
qDebug("FontListModel::setData() out of defined editable columns");
if (isLive())
{
if (idx.column() == FontListModel::FontUsable)
f.usable(!f.usable());
else if (idx.column() == FontListModel::FontEmbed)
f.embedPs(!f.embedPs());
else if (idx.column() == FontListModel::FontSubset)
f.subset(!f.subset());
else if (idx.column() == FontListModel::FontOutline)
f.outline(!f.outline());
else
qDebug("FontListModel::setData() out of defined editable columns");
}
else
{
if (idx.column() == FontListModel::FontEmbed)
{
if (m_embedMethod[idx.row()] == EmbedFont)
m_embedMethod[idx.row()] = DontEmbed;
else
m_embedMethod[idx.row()] = EmbedFont;
}
else if (idx.column() == FontListModel::FontSubset)
{
if (m_embedMethod[idx.row()] == SubsetFont)
m_embedMethod[idx.row()] = EmbedFont;
else
m_embedMethod[idx.row()] = SubsetFont;
}
else if (idx.column() == FontListModel::FontOutline)
{
if (m_embedMethod[idx.row()] == OutlineFont)
m_embedMethod[idx.row()] = SubsetFont;
else
m_embedMethod[idx.row()] = OutlineFont;
}else
qDebug("FontListModel::setData() out of defined editable columns");
}
emit dataChanged(index(idx.row(),FontListModel::FontEmbed), index(idx.row(),FontListModel::FontOutline));
 
emit dataChanged(index, index);
 
return true;
}
 
QString FontListModel::nameForIndex(const QModelIndex & index)
QString FontListModel::nameForIndex(const QModelIndex & idx)
{
return m_fonts.keys().at(index.row());
return m_font_names.at(idx.row());
}
/trunk/Scribus/scribus/fontlistmodel.h
35,15 → 35,16
FontListModel(QObject * parent = 0, ScribusDoc * doc = 0);
 
enum ColumnTypes {
FontName = 0,
FontUsable,
FontFamily,
FontStyle,
FontVariant,
FontUsable = 0,
FontName,
// FontFamily,
// FontStyle,
// FontVariant,
FontType,
FontFormat,
FontEmbed,
FontSubset,
FontOutline,
FontAccess,
FontInDoc,
FontFile,
72,10 → 73,10
are able to be edited by user. */
Qt::ItemFlags flags(const QModelIndex &index) const;
 
//! Returns Scribus fonts. TODO: is it required?
SCFonts fonts() { return m_fonts; };
//! Sets Scribus fonts and refresh the model. TODO: is it required?
void setFonts(SCFonts f);
//! Returns the font list
QList<QString> fonts() { return m_font_names; };
//! Sets font list and refresh the model. This will detach the model from the main Scribus font list!
void setFonts(QList<QString> f);
 
/*! Get the font name for current index.
\note Remember to use the mapToSource() if you're using QSortFilterProxyModel
82,11 → 83,23
*/
QString nameForIndex(const QModelIndex & index);
 
bool isLive() const { return m_embedMethod.count() == 0; }
private:
enum EmbedMethod {
DontEmbed = 0,
EmbedFont = 1,
SubsetFont = 2,
OutlineFont = 3
};
ScribusDoc * m_doc;
//! Scribus fonts. \note: It's shared!
SCFonts m_fonts;
QList<ScFace> m_font_values;
QList<QString> m_font_names;
QList<EmbedMethod> m_embedMethod;
//! Display icons by the Qt::DecorationRole
QPixmap ttfFont;
QPixmap otfFont;
/trunk/Scribus/scribus/fonts/CMakeLists.txt
9,9 → 9,10
scface.cpp
ftface.cpp
scface_ps.cpp
cff.cpp
sfnt.cpp
scface_ttf.cpp
scfontmetrics.cpp
sfnt.cpp
)
SET(SCRIBUS_FONTS_LIB "scribus_fonts_lib")
ADD_LIBRARY(${SCRIBUS_FONTS_LIB} STATIC ${SCRIBUS_FONTS_LIB_SOURCES})
/trunk/Scribus/scribus/fonts/cff.cpp
0,0 → 1,1744
//
// cff.cpp
// Scribus
//
// Created by Andreas Vox on 04.05.15.
//
//
 
#include "cff.h"
 
#include <cassert>
#include <cmath>
#include <QDebug>
 
namespace cff {
static const char* cffDictKeys[] = {
"version",
"Notice",
"FullName",
"FamilyName",
"Weight",
"FontBBox",
"BlueValues",
"OtherBlues",
"FamilyBlues",
"FamilyOtherBlues",
"StdHW",
"StdVW",
"",
"UniqueID",
"XUID",
"charset",
"Encoding",
"CharStrings",
"Private",
"Subrs",
"defaultWidthX",
"nominalWidthX"
};
static const char* cffDictKeys0c[] = {
"Copyright",
"IsFixedPitch",
"ItalicAngle",
"UnderlinePosition",
"UnderlineThickness",
"PaintType",
"CharstringType",
"FontMatrix",
"StrokeWidth",
"BlueScale",
"BlueShift",
"BlueFuzz",
"StemSnapH",
"StemSnapV",
"ForceBold",
"-Reserved-",
"-Reserved-",
"LanguageGroup",
"ExpansionFactor",
"InitialRandomSeed",
"SyntheticBase",
"PostScript",
"BaseFontName",
"BaseFontBlend",
"-Reserved-",
"-Reserved-",
"-Reserved-",
"-Reserved-",
"-Reserved-",
"-Reserved-",
"ROS",
"CIDFontVersion",
"CIDFontRevision",
"CIDFontType",
"CIDCount",
"UIDBase",
"FDArray",
"FDSelect",
"FontName"
};
static const char* cff_operator(cff::operator_type id)
{
if ((id & 0x0c00) == 0x0c00)
{
int idx = id & 0xff;
if (idx >= 0 && idx <= 38)
return cffDictKeys0c[idx];
else
return "";
}
else
{
if ( /* id >= 0 && */ id <= 21)
return cffDictKeys[id];
else
return "";
}
}
static const char* stdStrings[] = {
/* 0 */
".notdef",
"space",
"exclam",
"quotedbl",
"numbersign",
"dollar",
"percent",
"ampersand",
"quoteright",
"parenleft",
/* 10 */
"parenright",
"asterisk",
"plus",
"comma",
"hyphen",
"period",
"slash",
"zero",
"one",
"two",
"three",
"four",
"five",
"six",
"seven",
"eight",
"nine",
"colon",
"semicolon",
"less",
/* 30 */
"equal",
"greater",
"question",
"at",
"A",
"B",
"C",
"D",
"E",
"F",
"G",
"H",
"I",
"J",
"K",
"L",
"M",
"N",
"O",
"P",
/* 50 */
"Q",
"R",
"S",
"T",
"U",
"V",
"W",
"X",
"Y",
"Z",
"bracketleft",
"backslash",
"bracketrigh",
"asciicircum",
"underscore",
"quoteleft",
"a",
"b",
"c",
"d",
/* 70 */
"e",
"f",
"g",
"h",
"i",
"j",
"k",
"l",
"m",
"n",
"o",
"p",
"q",
"r",
"s",
"t",
"u",
"v",
"w",
"x",
/* 90 */
"y",
"z",
"braceleft",
"bar",
"braceright",
"asciitilde",
"exclamdown",
"cent",
"sterling",
"fraction",
/* 100 */
"yen",
"florin",
"section",
"currency",
"quotesingle",
"quotedblleft",
"guillemotleft",
"guilsinglleft",
"guilsinglright",
"fi",
"fl",
"endash",
"dagger",
"daggerdbl",
"periodcentered",
"paragraph",
"bullet",
"quotesinglbase",
"quotedblbase",
"quotedblright",
/* 120 */
"guillemotright",
"ellipsis",
"perthousand",
"questiondown",
"grave",
"acute",
"circumflex",
"tilde",
"macron",
"breve",
"dotaccent",
"dieresis",
"ring",
"cedilla",
"hungarumlaut",
"ogonek",
"caron",
"emdash",
"AE",
"ordfeminine",
/* 140 */
"Lslash",
"Oslash",
"OE",
"ordmasculine",
"ae",
"dotlessi",
"lslash",
"oslash",
"oe",
"germandbls",
"onesuperior",
"logicalnot",
"mu",
"trademark",
"Eth",
"onehalf",
"plusminus",
"Thorn",
"onequarter",
"divide",
/* 160 */
"brokenbar",
"degree",
"thorn",
"threequarters",
"twosuperior",
"registered",
"minus",
"eth",
"multiply",
"threesuperior",
"copyright",
"Aacute",
"Acircumflex",
"Adieresis",
"Agrave",
"Aring",
"Atilde",
"Ccedilla",
"Eacute",
"Ecircumflex",
/* 180 */
"Edieresis",
"Egrave",
"Iacute",
"Icircumflex",
"Idieresis",
"Igrave",
"Ntilde",
"Oacute",
"Ocircumflex",
"Odieresis",
"Ograve",
"Otilde",
"Scaron",
"Uacute",
"Ucircumflex",
"Udieresis",
"Ugrave",
"Yacute",
"Ydieresis",
"Zcaron",
/* 200 */
"aacute",
"acircumflex",
"adieresis",
"agrave",
"aring",
"atilde",
"ccedilla",
"eacute",
"ecircumflex",
"edieresis",
"egrave",
"iacute",
"icircumflex",
"idieresis",
"igrave",
"ntilde",
"oacute",
"ocircumflex",
"odieresis",
"ograve",
/* 220 */
"otilde",
"scaron",
"uacute",
"ucircumflex",
"udieresis",
"ugrave",
"yacute",
"ydieresis",
"zcaron",
"exclamsmall",
"Hungarumlautsmall",
"dollaroldstyle",
"dollarsuperior",
"ampersandsmall",
"Acutesmall",
"parenleftsuperior",
"parenrightsuperior",
"twodotenleader",
"onedotenleader",
"zerooldstyle",
/* 240 */
"oneoldstyle",
"twooldstyle",
"threeoldstyle",
"fouroldstyle",
"fiveoldstyle",
"sixoldstyle",
"sevenoldstyle",
"2eightoldstyle",
"nineoldstyle",
"commasuperior",
"threequartersemdash",
"periodsuperior",
"questionsmall",
"asuperior",
"bsuperior",
"centsuperior",
"dsuperior",
"esuperior",
"isuperior",
"lsuperior",
/* 260 */
"msuperior",
"nsuperior",
"osuperior",
"rsuperior",
"ssuperior",
"tsuperior",
"ff",
"ffi",
"ffl",
"parenleftinferior",
"parenrightinferior",
"Circumflexsmall",
"hyphensuperior",
"Gravesmall",
"Asmall",
"Bsmall",
"Csmall",
"Dsmall",
"Esmall",
"Fsmall",
/* 280 */
"Gsmall",
"Hsmall",
"Ismall",
"Jsmall",
"Ksmall",
"Lsmall",
"Msmall",
"Nsmall",
"Osmall",
"Psmall",
"Qsmall",
"Rsmall",
"Ssmall",
"Tsmall",
"Usmall",
"Vsmall",
"Wsmall",
"Xsmall",
"Ysmall",
"Zsmall",
/* 300 */
"colonmonetary",
"onefitted",
"rupiah",
"Tildesmall",
"exclamdownsmall",
"centoldstyle",
"Lslashsmall",
"Scaronsmall",
"Zcaronsmall",
"Dieresissmall",
"Brevesmall",
"Caronsmall",
"Dotaccentsmall",
"Macronsmall",
"iguredash",
"hypheninferior",
"goneksmall",
"Ringsmall",
"Cedillasmall",
"questiondownsmall",
/* 320 */
"oneeighth",
"hreeeighths",
"fiveeighths",
"seveneighths",
"onethird",
"twothirds",
"zerosuperior",
"foursuperior",
"fivesuperior",
"sixsuperior",
"sevensuperior",
"eightsuperior",
"ninesuperior",
"zeroinferior",
"oneinferior",
"twoinferior",
"threeinferior",
"fourinferior",
"fiveinferior",
"sixinferior",
/* 340 */
"seveninferior",
"eightinferior",
"nineinferior",
"centinferior",
"dollarinferior",
"periodinferior",
"commainferior",
"Agravesmall",
"Aacutesmall",
"Acircumflexsmall",
"Atildesmall",
"Adieresissmall",
"Aringsmall",
"AEsmall",
"Ccedillasmall",
"Egravesmall",
"Eacutesmall",
"Ecircumflexsmall",
"Edieresissmall",
"Igravesmall",
/* 360 */
"Iacutesmall",
"Icircumflexsmall",
"Idieresissmall",
"Ethsmall",
"Ntildesmall",
"Ogravesmall",
"Oacutesmall",
"Ocircumflexsmall",
"Otildesmall",
"Odieresissmall",
"OEsmall",
"Oslashsmall",
"Ugravesmall",
"Uacutesmall",
"Ucircumflexsmall",
"Udieresissmall",
"Yacutesmall",
"Thornsmall",
"Ydieresissmall",
"001.000",
/* 380 */
"001.001",
"001.002",
"001.003",
"Black",
"Bold",
"Book",
"Light",
"Medium",
"Regular",
"Roman",
/* 390 */
"Semibold"
};
bool CFF_Number::isCardinal() const
{
switch (type)
{
case cff_varnt_Error:
case cff_varnt_Real:
return false;
default:
return true;
}
}
double CFF_Number::toDouble() const
{
switch (type)
{
case cff_varnt_Real:
return card * std::pow(10.0, exponent);
default:
return toCardinal();
}
 
}
int CFF_Number::toCardinal() const
{
return card;
}
 
CFF::CFF() : bytes(), offsetSize(4)
{
for (int i = 0; i <= sid_last_std; ++i)
{
strings.append(stdStrings[i]);
sids[stdStrings[i]] = i;
}
}
CFF::CFF(const QByteArray& cff) : bytes(cff)
{
// read header
offsetSize = cff[cff_offSize];
uint pos = cff[cff_hdrSize];
qDebug() << "cff header" << offsetSize << "starts" << pos;
// read names
names = readIndex(pos);
// read top dicts
QList<QByteArray> topDicts = readIndex(pos);
for (int i = 0; i < names.length(); ++i)
{
QByteArray fontName = names[i];
qDebug() << i << fontName;
if (fontName.length() > 0 && fontName[0] != char(0))
{
fontTopDicts[fontName] = getDict(topDicts[i]);
uint privLength = fontTopDicts[fontName][18].array[0].toCardinal();
uint privOffset = fontTopDicts[fontName][18].array[1].toCardinal();
getDict(readSegment(privOffset, privLength));
}
}
// read strings
for (int i = 0; i <= sid_last_std; ++i)
{
strings.append(stdStrings[i]);
}
strings.append(readIndex(pos));
for (int i = 0; i < strings.length(); ++i)
{
// if ( i > sid_last_std)
// qDebug() << i << strings[i];
sids[strings[i]] = i;
}
// read global subroutines
globalSubr = readIndex(pos);
}
QByteArray CFF::readSegment(uint pos, uint size) const
{
return QByteArray::fromRawData(bytes.data() + pos, size);
}
uint CFF::readCard(uint pos) const
{
return static_cast<uchar>(bytes[pos]) << 8 | static_cast<uchar>(bytes[pos+1]);
}
QMap<operator_type,CFF_Variant> CFF::getDict(const QByteArray& dict) const
{
QMap<uint,CFF_Variant> result;
QList<CFF_Number> stack;
uint pos = 0;
while (pos < dict.length())
{
CFF_Number num = parseDictElement(dict, pos);
if (num.type == cff_varnt_Operator)
{
if (stack.length() == 1)
{
result[num.card] = CFF_Variant(stack[0]);
}
else if (stack.length() > 0)
{
result[num.card] = CFF_Variant(stack);
}
else
{
/* error */
}
stack = QList<CFF_Number>();
}
else
{
stack.append(num);
}
}
if (stack.length() > 0)
{
/* error */
}
// TODO: adapt variant type according to operator
return result;
}
CFF_Number CFF::parseDictElement(const QByteArray& dict, uint& pos) const
{
uint code = dict[pos];
switch (code)
{
case cff_dict_TwoBytes:
code = (code << 8) | dict[++pos];
break;
case cff_dict_Card16:
case cff_dict_Card32:
return parseCard(dict, pos);
case cff_dict_Real:
return parseReal(dict, pos);
 
default:
if (code >= cff_dict_minOperand)
{
return parseCard(dict, pos);
}
break;
}
++pos;
CFF_Number num;
num.type = cff_varnt_Operator;
num.card = code;
qDebug() << "parsed operator" << cff_operator(code) << "(" << code << ")";
return num;
}
CFF_Number CFF::parseCard(const QByteArray& dict, uint& pos) const
{
CFF_Number result;
result.type = cff_varnt_Card;
result.exponent = 0;
uint start=pos;
uchar b0 = dict[pos++];
uchar b1,b2,b3,b4;
if (b0 == cff_dict_Card16)
{
b1 = dict[pos++];
b2 = dict[pos++];
result.card = static_cast<qint16>((b1 << 8) | b2);
}
else if (b0 == cff_dict_Card32)
{
b1 = dict[pos++];
b2 = dict[pos++];
b3 = dict[pos++];
b4 = dict[pos++];
result.card = static_cast<qint32>((b1 << 24) | (b2 << 16) | (b3 << 8) | b4);
}
else if (b0 < cff_dict_minOperand)
{
/* error */
}
else if (b0 <= cff_dict_maxSmallCard)
{
result.card = b0 + cff_dict_biasSmallCard;
}
else if (b0 <= cff_dict_maxPosCard)
{
b1 = dict[pos++];
result.card = (b0 - cff_dict_minPosCard) * 256 + b1 + cff_dict_biasPosCard;
}
else if (b0 <= cff_dict_maxNegCard)
{
b1 = dict[pos++];
result.card = (cff_dict_minNegCard - b0) * 256 - b1 + cff_dict_biasNegCard;
}
else
{
/* error */
}
qDebug() << "parsed" << QByteArray::fromRawData(dict.data()+start, pos-start).toHex() << "to card " << result.card;
return result;
}
CFF_Number CFF::parseReal(const QByteArray& dict, uint& pos) const
{
assert( dict[pos] == cff_dict_Real );
uint start = pos++;
 
CFF_Number result;
result.type = cff_varnt_Real;
result.card = 0;
result.exponent = 0;
bool upperNibble = false;
bool haveSeenMinus = false;
bool haveSeenE = false;;
bool haveSeenEminus = false;
int decimalPointAt = -1;
uchar nibble, twoNibbles;
do
{
// get nibble
if (upperNibble)
{
nibble = twoNibbles & 0x0f;
upperNibble = false;
}
else
{
twoNibbles = dict[pos++];
nibble = twoNibbles >> 4;
upperNibble = true;
}
// decode
switch (nibble)
{
case cff_nibble_Minus:
haveSeenMinus = !haveSeenMinus;
break;
case cff_nibble_Point:
decimalPointAt = 0;
break;
case cff_nibble_PosExp:
haveSeenE = true;
break;
case cff_nibble_NegExp:
haveSeenEminus = true;
break;
case cff_nibble_End:
break;
default:
if (nibble > cff_nibble_maxDigit)
{
/* error */
}
else if (haveSeenE || haveSeenEminus)
{
result.exponent *= 10;
result.exponent += nibble;
}
else
{
result.card *= 10;
result.card += nibble;
if (decimalPointAt >= 0)
{
++decimalPointAt;
}
}
break;
}
} while (nibble != cff_nibble_End);
// finish
if (haveSeenMinus)
result.card = -result.card;
if (haveSeenEminus)
result.exponent = -result.exponent;
if (decimalPointAt > 0)
result.exponent -= decimalPointAt;
qDebug() << "parsed" << QByteArray::fromRawData(dict.data()+start, pos-start).toHex() << "to real " << result.card << "E" << result.exponent;
 
return result;
}
QList<QByteArray> CFF::readIndex(uint& pos) const
{
qDebug() << "read INDEX @" << pos;
QList<QByteArray> result;
uint N = readCard(pos);
pos += 2;
uint offSize = bytes[pos++];
uint dataStart = pos + offSize * (N+1) - 1;
qDebug() << "size" << N << "offsetsize" << offSize << "dataStart" << dataStart;
uint start = 0;
uint end;
for (int c = 0; c < offSize; ++c)
{
start = start << 8 | (uchar) bytes[pos++];
}
start += dataStart;
for (int i = 0; i < N; ++i)
{
end = 0;
for (int c = 0; c < offSize; ++c)
{
end = end << 8 | (uchar) bytes[pos++];
}
end += dataStart;
result.append(readSegment(start, end-start));
start = end;
}
pos = end;
qDebug() << "INDEX ends @" << end;
return result;
}
QList<uint> CFF::readEncoding(uint& pos) const
{
QList<uint> result;
for(int i = 0; i < 256; ++i)
result.append(0);
uchar format = bytes[pos++];
uchar N = bytes[pos++];
int gid;
uchar code;
switch (format)
{
case 0x0:
case 0x80:
for (gid = 1; gid <= N; ++gid)
{
code = bytes[pos++];
if (result[code] == 0)
{
result[code] = gid;
}
}
break;
case 0x1:
case 0x81:
gid = 1;
for (int r = 0; r < N; ++r)
{
uchar first = bytes[pos++];
uchar nLeft = bytes[pos++];
for (code = first; code <= first + nLeft; ++code)
{
if (result[code] == 0)
{
result[code] = gid;
}
++gid;
}
}
break;
}
if (format >= 0x80)
{
uchar nSupplements = bytes[pos++];
for (int i = 0; i < nSupplements; ++i)
{
code = bytes[pos++];
gid = readCard(pos);
pos += 2;
result[code] = gid;
}
}
return result;
}
QList<sid_type> CFF::readCharset(uint nGlyphs, uint& pos) const
{
QList<sid_type> result;
result.append(0); // sid for .notdef
uchar format = bytes[pos++];
sid_type first;
uchar nLeft1;
uint nLeft2;
switch (format)
{
case 0:
for (int gid = 1; gid < nGlyphs; ++gid)
{
result.append(readCard(pos));
pos += 2;
}
break;
case 1:
while (result.length() < nGlyphs)
{
first = readCard(pos);
pos += 2;
nLeft1 = bytes[pos++];
for (sid_type sid = first; sid <= first + nLeft1; ++sid)
{
result.append(sid);
}
}
break;
case 2:
while (result.length() < nGlyphs)
{
first = readCard(pos);
pos += 2;
nLeft2 = readCard(pos);
pos += 2;
for (sid_type sid = first; sid <= first + nLeft2; ++sid)
{
result.append(sid);
}
}
break;
}
return result;
}
QByteArray CFF::dump(const CFF_Variant& var) const
{
QByteArray result;
switch(var.type)
{
case cff_varnt_Error:
result += "ERROR";
break;
case cff_varnt_Card:
case cff_varnt_Bool:
case cff_varnt_SID:
result += QByteArray::number(var.array[0].toCardinal());
break;
case cff_varnt_Real:
result += QByteArray::number(var.array[0].toDouble());
break;
case cff_varnt_Operator:
result += string(var.array[0].toCardinal());
result += " (";
result += QByteArray::number(var.array[0].toCardinal());
result +=")";
break;
case cff_varnt_Delta:
result += "Delta";
/* fall thru */
case cff_varnt_Array:
result += "[";
for (int i = 0; i < var.array.length(); ++i)
{
result += QByteArray::number(var.array[i].toDouble());
result += ", ";
}
result.chop(2);
result += "]";
break;
}
return result;
}
void CFF::dump()
{
qDebug() << "CFF" << fontTopDicts.count() << "fonts, size =" << bytes.size() << "offset size=" << offsetSize;
QMap<QByteArray,QMap<operator_type,CFF_Variant> >::Iterator it;
for (it = fontTopDicts.begin(); it != fontTopDicts.end(); ++it)
{
qDebug() << "Font" << it.key() << ":";
QMap<operator_type, CFF_Variant>::Iterator it2;
for (it2= it.value().begin(); it2 != it.value().end(); ++it2)
{
qDebug() << "\t" << cff_operator(it2.key()) << "=" << dump(it2.value());
}
}
}
static QByteArray num(uint n)
{
return QByteArray::number(n);
}
static void write(QDataStream& out, const QByteArray& data)
{
out.writeRawData(data.data(), data.length());
}
static void dumpData(const QList<QByteArray>& index, QDataStream& out, const QByteArray& indent)
{
for (int i = 0; i < index.length(); ++i)
{
write(out, indent);
write(out, "<data idx='");
write(out, num(i));
write(out, "' length='");
write(out, num(index[i].length()));
write(out, "' >");
write(out, index[i].toHex());
write(out, "</data>\n");
}
}
 
static void dumpStrings(const QList<QByteArray>& index, QDataStream& out, const QByteArray& indent)
{
for (int i = 0; i < index.length(); ++i)
{
write(out, indent);
write(out, "<string idx='");
write(out, num(i));
write(out, "' length='");
write(out, num(index[i].length()));
write(out, "' >");
write(out, index[i]);
write(out, "</string>\n");
}
}
 
static void dumpDict(const CFF& cff, const QMap<operator_type,CFF_Variant>& dict, QDataStream& out, const QByteArray& indent)
{
QMap<operator_type,CFF_Variant>::ConstIterator it;
for (it = dict.cbegin(); it != dict.cend(); ++it)
{
write(out, indent);
write(out, "<keyvalue key='");
write(out, cff_operator(it.key()));
write(out, "' value='");
write(out, cff.dump(it.value()));
write(out, "' />\n");
}
}
void CFF::dump(QDataStream& out) const
{
write(out, "<CFF version='1.0' offsetSize='" + num(offsetSize) + "' >\n");
for (int f = 0; f < names.length(); ++f)
{
QByteArray font = names[f];
QMap<operator_type, CFF_Variant> topDict = fontTopDicts[font];
write(out, " <Font name='" + font + "' >\n");
write(out, " <TopDict>");
dumpDict(*this, topDict, out, " ");
write(out, " </TopDict>\n");
uint pos;
if (topDict.contains(cff_dict_Encoding))
{
uint encOffset = topDict[cff_dict_Encoding].array[0].toCardinal();
if (encOffset > 4)
{
write(out, " <Encoding>\n ");
pos = encOffset;
QList<uint> enc = readEncoding(pos);
write(out, readSegment(encOffset, pos-encOffset).toHex());
write(out, "\n </Encoding>\n");
}
else
write(out, " <Encoding predefined='" + num(encOffset) + "' />\n");
}
pos = topDict[cff_dict_CharStrings].array[0].toCardinal();
QList<QByteArray> charStrings = readIndex(pos);
uint nglyphs = charStrings.length();
if (topDict.contains(cff_dict_charset))
{
uint charsetOffset = topDict[cff_dict_charset].array[0].toCardinal();
if (charsetOffset > 4)
{
pos = charsetOffset;
QList<sid_type> charset = readCharset(nglyphs, pos);
write(out, " <charset>\n");
for (int i=0; i < charset.length(); ++i)
{
write(out, " <char gid='" + num(i) + "' sid='" + num(charset[i]) + "' >");
write(out, string(charset[i]) + "</char>\n");
}
write(out, " </charset>\n");
}
}
write(out, " <CharStrings>\n");
dumpData(charStrings, out, " ");
write(out, " </CharStrings>\n");
uint privateLength = topDict[cff_dict_Private].array[0].toCardinal();
uint privateOffset = topDict[cff_dict_Private].array[1].toCardinal();
write(out, " <PrivateDict offset='" + num(privateOffset) + "' length='" + num(privateLength) + ">\n");
QMap<operator_type,CFF_Variant> privateDict = getDict(readSegment(privateOffset, privateLength));
dumpDict(*this, privateDict, out, " ");
write(out, " </PrivateDict>\n");
if (privateDict.contains(cff_dict_Subrs))
{
uint subrsOffset = privateDict[cff_dict_Subrs].array[0].toCardinal();
pos = privateOffset + subrsOffset;
QList<QByteArray> localSubrs = readIndex(pos);
write(out, " <LocalSubrs offset='" + num(subrsOffset) + "' length='" + num(pos-subrsOffset) + "' >\n");
dumpData(localSubrs, out, " ");
write(out, " </LocalSubrs>\n");
}
write(out, " </Font>\n");
}
write(out, " <Strings>\n");
dumpStrings(strings, out, " ");
write(out, " </Strings>\n");
write(out, " <GlobalSubrs>\n");
dumpData(globalSubr, out, " ");
write(out, " </GlobalSubrs>\n");
write(out, "</CFF>\n");
}
/// encodes 'value' as exactly 'nBytes' bytes in Big Endian
static QByteArray encodeBE(int nbytes, uint value)
{
QByteArray result;
while (nbytes-- > 0)
{
result.prepend(static_cast<uchar>(value & 0xFF));
value >>= 8;
}
return result;
}
/// calculates the required offset size to represent 'dataLength'
static int requiredOffsetSize(int dataLength)
{
if (dataLength < 255)
return 1;
else if (dataLength < 65535)
return 2;
else if (dataLength < 0xFFFFFF)
return 3;
else
return 4;
}
static void writeCard(QByteArray& bytes, int val)
{
int biasedVal = val - cff_dict_biasSmallCard;
if (biasedVal >= cff_dict_minOperand && biasedVal <= cff_dict_maxSmallCard)
{
bytes.append((char) biasedVal);
return;
}
if (val > 0)
{
biasedVal = val - cff_dict_biasPosCard;
if (biasedVal >= 0 && biasedVal <= 1023)
{
biasedVal += (cff_dict_minPosCard << 8);
bytes.append(encodeBE(2, biasedVal));
qDebug() << "writeCard" << val << "as" << encodeBE(2, biasedVal).toHex();
return;
};
}
else
{
biasedVal = -val + cff_dict_biasNegCard;
if (biasedVal >= 0 && biasedVal <= 1023)
{
biasedVal += (cff_dict_minNegCard << 8);
bytes.append(encodeBE(2, biasedVal));
qDebug() << "writeCard" << val << "as" << encodeBE(2, biasedVal).toHex();
return;
};
}
if (val >= -32768 && val <= 32767)
{
bytes.append((char) cff_dict_Card16);
bytes.append(encodeBE(2, val));
}
else
{
bytes.append((char) cff_dict_Card32);
bytes.append(encodeBE(4, val));
}
}
/// creates one byte per digit (not ASCII!)
static QByteArray bsdNibbles(long long val)
{
QByteArray result;
if (val < 0)
val = -val;
do {
result.prepend((char) (val % 10));
val /= 10;
}
while (val != 0);
return result;
}
static void writeReal(QByteArray& bytes, CFF_Number num)
{
QByteArray bsd;
if (num.card < 0)
{
bsd.append((char) cff_nibble_Minus);
bsd.append(bsdNibbles(-num.card));
}
else
{
bsd.append(bsdNibbles(num.card));
}
if (num.exponent < 0)
{
bsd.append((char) cff_nibble_NegExp);
bsd.append(bsdNibbles(-num.exponent));
}
else if (num.exponent > 0)
{
bsd.append((char) cff_nibble_PosExp);
bsd.append(bsdNibbles(num.exponent));
}
bsd.append((char) cff_nibble_End);
if (bsd.length() % 2 == 1)
bsd.append((char) cff_nibble_End);
uint start = bytes.length();
bytes.append((char) cff_dict_Real);
for (int i= 0; i < bsd.length(); i += 2)
{
bytes.append((char) ((bsd[i] << 4) | bsd[i+1]));
}
qDebug() << "writeReal" << num.card << "E" << num.exponent << "as" << bytes.mid(start).toHex();
}
uint CFF::writeSegment(const QByteArray& data)
{
uint result = bytes.length();
bytes.append(data);
return result;
}
sid_type CFF::createSid(const QByteArray& str)
{
sid_type result;
if (!sids.contains(str))
{
result = strings.length();
strings.append(str);
sids[str] = result;
qDebug() << "new SID" << result << "for" << str;
}
else
{
result = sids[str];
}
return result;
}
uint CFF::writeTopDict(QByteArray name,
QMap<operator_type, CFF_Variant> dict,
QList<QByteArray> oldStrings,
QHash<operator_type, uint>& patchAddresses)
{
offsetSize = 4;
names.append(name);
fontTopDicts[name] = dict;
bytes.append((char) 1);
bytes.append((char) 0); // format 1.0
bytes.append((char) 4); // header length 4
bytes.append((char) 4); // offsetSize 4
// write Name index
bytes.append(encodeBE(2,1)); // count
assert (name.length() < 255);
bytes.append(encodeBE(1, 1)); // offSize
bytes.append(encodeBE(1, 1)); // offset 1
bytes.append(encodeBE(1, 1 + name.length())); // offset 2
bytes.append(name);
// write TopDict index
QByteArray topDict = makeDict(dict, oldStrings, patchAddresses);
int offSize = requiredOffsetSize(topDict.length());
bytes.append(encodeBE(2, 1)); // count
bytes.append(encodeBE(1, offSize)); // offSize
bytes.append(encodeBE(offSize, 1)); // offset 1
bytes.append(encodeBE(offSize, 1 + topDict.length())); // offset 2
uint start = bytes.size();
bytes.append(topDict);
return start;
}
QByteArray CFF::makeDict(QMap<operator_type, CFF_Variant> dict,
QList<QByteArray> oldStrings,
QHash<operator_type, uint>& patchAddresses)
{
QByteArray result;
if (dict.contains(cff_dict_ROS))
{
// pull to front
QList<CFF_Number> ros = dict[cff_dict_ROS].array;
sid_type sid = ros[0].toCardinal();
sid = createSid(oldStrings[sid]);
writeCard(result, sid);
sid = ros[1].toCardinal();
sid = createSid(oldStrings[sid]);
writeCard(result, sid);
writeCard(result, ros[2].toCardinal());
result.append(encodeBE(2, cff_dict_ROS));
 
}
if (dict.contains(cff_dict_SyntheticBase))
{
// pull to front
writeCard(result, dict[cff_dict_ROS].array[0].toCardinal());
result.append(encodeBE(2, cff_dict_SyntheticBase));
}
QMap<operator_type, CFF_Variant>::Iterator it;
for (it = dict.begin(); it != dict.end(); ++it)
{
QList<CFF_Number> arr = it.value().array;
switch (it.key())
{
case cff_dict_ROS:
case cff_dict_SyntheticBase:
/* already done */
break;
case cff_dict_charset:
case cff_dict_Encoding:
case cff_dict_CharStrings:
case cff_dict_FDArray:
case cff_dict_FDSelect:
case cff_dict_Subrs:
/* remember offset */
patchAddresses[it.key()] = result.length();
/* write 32 bit offset */
result.append((char) cff_dict_Card32);
result.append(encodeBE(4, arr[0].toCardinal()));
break;
case cff_dict_Private:
/* remember offset */
patchAddresses[it.key()] = result.length();
/* write 32 bit length */
result.append((char) cff_dict_Card32);
result.append(encodeBE(4, arr[0].toCardinal()));
/* write 32 bit offset */
result.append((char) cff_dict_Card32);
result.append(encodeBE(4, arr[1].toCardinal()));
break;
case cff_dict_version:
case cff_dict_Notice:
case cff_dict_Copyright:
case cff_dict_FullName:
case cff_dict_FamilyName:
case cff_dict_Weight:
case cff_dict_PostScript:
case cff_dict_BaseFontName:
case cff_dict_FontName:
{
/* write SID */
sid_type sid = arr[0].toCardinal();
sid = createSid(oldStrings[sid]);
qDebug() << "writeDict SID" << arr[0].toCardinal() << "-->" << sid;
writeCard(result, sid);
}
break;
default:
/* write numbers */
for (int i = 0; i < arr.length(); ++i)
{
if (arr[i].type == cff_varnt_Real)
writeReal(result, arr[i]);
else
writeCard(result, arr[i].toCardinal());
}
break;
}
// write operator
if (it.key() >= 0x0c00)
{
result.append(encodeBE(2, it.key()));
}
else
{
result.append((char) it.key());
}
}
return result;
}
void CFF::patch(QHash<operator_type, uint> patchPositions,
uint patchOffset,
operator_type op,
uint offset,
uint length)
{
if (patchPositions.contains(op))
{
uint pos = patchOffset + patchPositions[op];
uchar c;
switch (op)
{
case cff_dict_charset:
case cff_dict_Encoding:
case cff_dict_CharStrings:
case cff_dict_Subrs:
case cff_dict_FDArray:
case cff_dict_FDSelect:
assert (bytes[pos] == (char) cff_dict_Card32);
++pos;
bytes.replace(pos, 4, encodeBE(4, offset));
qDebug() << "patch" << cff_operator(op) << "offset @" << pos << offset;
break;
case cff_dict_Private:
c = bytes[pos];
if (c == cff_dict_Card16)
{
if (length > 0)
{
bytes.replace(pos+1, 2, encodeBE(2, length));
qDebug() << "patch priv short length @" << (pos+1) << length;
}
pos += 3;
}
else if (c == cff_dict_Card32)
{
if (length > 0)
{
bytes.replace(pos+1, 4, encodeBE(4, length));
qDebug() << "patch priv length @" << (pos+1) << length;
}
pos += 5;
}
else if (c >= cff_dict_minOperand && c <= cff_dict_maxSmallCard)
{
pos += 1;
}
else if (c >= cff_dict_minPosCard && c <= cff_dict_maxNegCard)
{
pos += 2;
}
else
{
/* error */
}
assert (bytes[pos] == (char) cff_dict_Card32);
++pos;
bytes.replace(pos, 4, encodeBE(4, offset));
qDebug() << "patch priv offset @" << pos << offset;
break;
default:
/* error */
break;
}
}
}
QByteArray CFF::makeIndex(QList<QByteArray> data) const
{
QByteArray result;
uint size = 0;
for (int i = 0; i < data.length(); ++i)
{
size += data[i].size();
}
int offSize = requiredOffsetSize(size);
result.append(encodeBE(2, data.length())); // count
if (data.length() == 0)
return result;
result.append(encodeBE(1, offSize)); // offSize
uint offset = 1;
result.append(encodeBE(offSize, offset)); // offset 1
for (int i=0; i < data.length(); ++i)
{
offset += data[i].length();
result.append(encodeBE(offSize, offset));
}
for (int i=0; i < data.length(); ++i)
{
result.append(data[i]);
}
return result;
}
 
QByteArray CFF::makeCharset(QList<sid_type> sids) const
{
QByteArray result;
// we won't bother with ranges for now
result.append('\0'); // format 0
for (int i = 1; i < sids.length(); ++i)
{
result.append(encodeBE(2, sids[i]));
}
return result;
}
QByteArray CFF::makeEncoding(QList<uint> encoding) const
{
QByteArray result;
#ifdef UNTESTET_CFF_MAKEENCODING
QMap<uint, uchar> codes;
QMap<uchar, uint> supplements;
uint maxGid = 0;
for (uchar c = 0; c < encoding.length(); ++c)
{
uint gid = encoding[c];
if (gid > maxGid && gid < 256)
maxGid = gid;
if (gid != 0)
{
if (gid >= 256 || codes.contains(gid))
supplements[c] = gid;
else
codes[gid] = c;
}
}
result.append('\0');
result.append((uchar) (maxGid+1));
for (uint gid = 0; gid <= maxGid; ++gid);
{
if (codes.contains(gid))
result.append(codes[gid]);
else
result.append('\0'); // this is probably not correct
}
QMap<uchar, uint>::Iterator it;
result.append(supplements.count());
for (it = supplements.begin(); it != supplements.end(); ++it)
{
result.append(it.key());
result.append(encodeBE(2, it.value()));
}
#else
result.append((char) 0x80); // format 0 with supplements
result.append((char) 0); // no encoded glyphs except supplements
result.append((char) encoding.length()); // all supplements
for (uint c = 0; c < encoding.length(); ++c)
{
result.append((uchar)c);
result.append(encodeBE(2, encoding[c]));
}
#endif
return result;
}
CFF CFF::extractSubset(uint faceIndex,
QList<uint> cids) const
{
uint pos;
// get top dict
QByteArray fontName = names[faceIndex];
QMap<operator_type, CFF_Variant> topDict = fontTopDicts[fontName];
// get charstrings
QList<QByteArray> charStrings;
pos = topDict[cff_dict_CharStrings].array[0].toCardinal();
charStrings = readIndex(pos);
// get encoding (optional, maybe predefined 0-1)
QList<uint> encoding;
if (topDict.contains(cff_dict_Encoding))
{
uint offset = topDict[cff_dict_Encoding].array[0].toCardinal();
if (offset > 1)
{
pos = offset;
encoding = readEncoding(pos);
// encodingBytes = readSegment(offset, pos-offset);
}
else
encoding.append(offset);
}
// get charmap (optional, maybe predefined 0-2)
QList<sid_type> charset;
if (topDict.contains(cff_dict_charset))
{
uint offset = topDict[cff_dict_charset].array[0].toCardinal();
if (offset > 2)
{
pos = offset;
charset = readCharset(charStrings.count(), pos);
// charsetBytes = readSegment(offset, pos-offset);
}
else
charset.append(offset);
}
// get private dict
QList<CFF_Number> lengthOffset = topDict[cff_dict_Private].array;
QMap<operator_type, CFF_Variant> privateDict = getDict(readSegment(lengthOffset[1].toCardinal(), lengthOffset[0].toCardinal()));
// get local subr (optional)
QList<QByteArray> localSubrs;
if (privateDict.contains(cff_dict_Subrs))
{
pos = lengthOffset[1].toCardinal() + privateDict[cff_dict_Subrs].array[0].toCardinal();
localSubrs = readIndex(pos);
}
// now create new font
CFF result;
result.globalSubr = globalSubr; // no changes
// subset
if (cids.length() > 0)
{
// normalize .notdef
cids.removeAll(0);
cids.prepend(0);
// forget encoding
topDict.remove(cff_dict_Encoding);
encoding = QList<uint>();
// new charset
QList<sid_type> newCharset;
// new charStrings
QList<QByteArray> newCharStrings;
 
for (int i = 0; i < cids.length(); ++i)
{
sid_type gid = cids[i];
sid_type sid = charset[gid];
if (sid < strings.length())
{
sid = result.createSid(strings[sid]);
}
newCharset.append(sid);
newCharStrings.append(charStrings[gid]);
}
charset = newCharset;
charStrings = newCharStrings;
}
else if (charset.length() > 1)
{
// copy over needed strings
for (int i = 0; i < charset.length(); ++i)
{
sid_type cid = charset[i];
if (cid < strings.length())
{
cid = result.createSid(strings[cid]);
}
charset[i] = cid;
}
}
// create new private dict
QHash<operator_type, uint> privatePatches;
QByteArray privateBytes = result.makeDict(privateDict, strings, privatePatches);
 
// write new header, name and topdict, remember offset positions for patching
QHash<operator_type, uint> patchPositions;
uint topDictOffset = result.writeTopDict(fontName, topDict, strings, patchPositions);
// write strings
// makeDict() needs to be called before this in order to create SIDs for used strings
result.writeSegment(makeIndex(result.strings.mid(sid_last_std + 1)));
// write global subr (required but maybe empty)
result.writeSegment(makeIndex(globalSubr));
 
// write encoding
uint encodingOffset = encoding.size() > 1? result.writeSegment(makeEncoding(encoding)) : encoding.size() == 1? encoding[0] : 0;
// write charset
uint charsetOffset = charset.size() > 1? result.writeSegment(makeCharset(charset)) : charset.size() == 1? charset[0] : 0;
// write charstrings
uint charStringsOffset = result.writeSegment(makeIndex(charStrings));
// write private dict
uint privateOffset = result.writeSegment(privateBytes);
// write local subr
if (localSubrs.size() > 0)
{
uint localSubrOffset = result.writeSegment(makeIndex(localSubrs));
 
result.patch(privatePatches, privateOffset, cff_dict_Subrs, localSubrOffset - privateOffset);
}
// patch topdict offset positions for charset, encoding, charstrings, private
result.patch(patchPositions, topDictOffset, cff_dict_charset, charsetOffset);
result.patch(patchPositions, topDictOffset, cff_dict_Encoding, encodingOffset);
result.patch(patchPositions, topDictOffset, cff_dict_CharStrings, charStringsOffset);
result.patch(patchPositions, topDictOffset, cff_dict_Private, privateOffset, privateBytes.length());
// no FDArray and FDSelect yet
return result;
}
QByteArray extractFace(const QByteArray& cff, int faceIndex)
{
return CFF(cff).extractSubset(faceIndex, QList<uint>()).data();
}
QByteArray subsetFace(const QByteArray& cff, QList<uint> cids)
{
return CFF(cff).extractSubset(0, cids).data();
}
} // namespace
/trunk/Scribus/scribus/fonts/cff.h
0,0 → 1,245
//
// cff.h
// Scribus
//
// Created by Andreas Vox on 04.05.15.
//
//
 
#ifndef Scribus__cff_h
#define Scribus__cff_h
 
 
#include "scribusapi.h"
 
#include <QByteArray>
#include <QDataStream>
#include <QHash>
#include <QList>
#include <QMap>
#include <QString>
 
#include <ft2build.h>
#include FT_FREETYPE_H
 
namespace cff {
typedef quint16 sid_type;
typedef uint operator_type;
enum sid_range {
sid_min = 0,
sid_last_std = 390,
sid_max = 64999,
sid_max1 = 65000
};
enum CFF_Header_Format {
cff_major = 0,
cff_minor = 1,
cff_hdrSize = 2,
cff_offSize = 3
};
enum CFF_INDEX_Format {
cff_idx_count = 0,
cff_idx_offsize = 2,
cff_idx_offsets = 3
};
enum cff_Real_Format {
cff_nibble_maxDigit = 9,
cff_nibble_Point = 10,
cff_nibble_PosExp = 11,
cff_nibble_NegExp = 12,
cff_nibble_Reserved = 13,
cff_nibble_Minus = 14,
cff_nibble_End = 15
};
enum CFF_DICT_Format {
cff_dict_version = 0x00,
cff_dict_Notice = 0x01,
cff_dict_FullName = 0x02,
cff_dict_FamilyName = 0x03,
cff_dict_Weight = 0x04,
cff_dict_FontBBox = 0x05,
cff_dict_BlueValues = 0x06,
cff_dict_OtherBlues = 0x07,
cff_dict_FamilyBlues = 0x08,
cff_dict_FamilyOtherBlues = 0x09,
cff_dict_StdHW = 0x0a,
cff_dict_StdVW = 0x0b,
cff_dict_TwoBytes = 12,
cff_dict_UniqueID = 0x0d,
cff_dict_XUID = 0x0e,
cff_dict_charset = 0x0f,
cff_dict_Encoding = 0x10,
cff_dict_CharStrings = 0x11,
cff_dict_Private = 0x12,
cff_dict_Subrs = 0x13,
cff_dict_defaultWidthX = 0x14,
cff_dict_nominalWidthX = 0x15,
// 0x16+ reserved
cff_dict_Card16 = 28,
cff_dict_Card32 = 29,
cff_dict_Real = 30,
cff_dict_minOperand = 32,
cff_dict_maxSmallCard = 246,
cff_dict_biasSmallCard = -139,
cff_dict_minPosCard = 247,
cff_dict_maxPosCard = 250,
cff_dict_biasPosCard = 108,
cff_dict_minNegCard = 251,
cff_dict_maxNegCard = 254,
cff_dict_biasNegCard = -108,
cff_dict_Copyright = 0x0c00,
cff_dict_isFixedPitch = 0x0c01,
cff_dict_ItalicAngle = 0x0c02,
cff_dict_UnderlinePosition = 0x0c03,
cff_dict_UnderlineThickness = 0x0c04,
cff_dict_CharstringType = 0x0c06,
cff_dict_FontMatrix = 0x0c07,
cff_dict_StrokeWidth = 0x0c08,
cff_dict_BlueScale = 0x0c09,
cff_dict_BlueShift = 0x0c0a,
cff_dict_BlueFuzz = 0x0c0b,
cff_dict_StemSnapH = 0x0c0c,
cff_dict_StemSnapV = 0x0c0d,
cff_dict_ForceBold = 0x0c0e,
// 0c 0f -Reserved-
// 0c 10 -Reserved-
cff_dict_LanguageGroup = 0x0c11,
cff_dict_ExpansionFactor = 0x0c12,
cff_dict_initialRandomSeed = 0x0c13,
cff_dict_SyntheticBase = 0x0c14,
cff_dict_PostScript = 0x0c15,
cff_dict_BaseFontName = 0x0c16,
cff_dict_BaseFontBlend = 0x0c17,
// 0c 18 -Reserved-
// 0c 19 -Reserved-
// 0c 1a -Reserved-
// 0c 1b -Reserved-
// 0c 1c -Reserved-
// 0c 1d -Reserved-
cff_dict_ROS = 0x0c1e,
cff_dict_CIDFontVersion = 0x0c1f,
cff_dict_CIDFontRevision = 0x0c20,
cff_dict_CIDFontType = 0x0c21,
cff_dict_CIDCount = 0x0c22,
cff_dict_UIDBase = 0x0c23,
cff_dict_FDArray = 0x0c24,
cff_dict_FDSelect = 0x0c25,
cff_dict_FontName = 0x0c26
};
enum CFF_Variant_Type {
cff_varnt_Error = 0,
cff_varnt_Bool = 1,
cff_varnt_Card = 2,
cff_varnt_SID = 3,
cff_varnt_Real = 4,
cff_varnt_Array = 5,
cff_varnt_Delta = 6,
cff_varnt_Operator = 7
};
 
struct CFF_Number {
long long card;
int exponent;
uchar type;
bool isCardinal() const;
double toDouble() const;
int toCardinal() const;
};
 
struct CFF_Variant {
uchar type;
QList<CFF_Number> array;
CFF_Variant() : type(cff_varnt_Error) {}
CFF_Variant(CFF_Number val) : type(val.type), array() { array.append(val); }
CFF_Variant(QList<CFF_Number> arr) : type(cff_varnt_Array), array(arr) {}
};
class CFF {
public:
/// For creating new CFF fonts
CFF();
CFF(const QByteArray& cff);
 
uint readCard(uint pos) const;
QByteArray readSegment(uint pos, uint size) const;
QMap<operator_type,CFF_Variant> getDict(const QByteArray& dict) const;
QList<QByteArray> readIndex(uint& pos) const;
CFF extractSubset(uint faceIndex, QList<uint> cids) const;
void dump();
QByteArray dump(const CFF_Variant& var) const;
const QByteArray& data() const {
return bytes;
}
QList<QByteArray> fontNames() const {
return fontTopDicts.keys();
}
uint offset(uint unscaled)
{
return unscaled * offsetSize;
}
QByteArray string(sid_type sid) const {
return sid < strings.length()? strings[sid] : "";
}
sid_type sid(const QByteArray str) const {
return sids.contains(str)? sids[str] : sid_max1;
}
void dump(QDataStream& out) const;
private:
QByteArray bytes;
uint offsetSize;
QList<QByteArray> names;
QMap<QByteArray, QMap<uint,CFF_Variant> > fontTopDicts;
QList<QByteArray> strings;
QHash<QByteArray,uint> sids;
QList<QByteArray> globalSubr;
sid_type createSid(const QByteArray& str);
CFF_Number parseDictElement(const QByteArray& dict, uint& pos) const;
CFF_Number parseReal(const QByteArray& dict, uint& pos) const;
CFF_Number parseCard(const QByteArray& dict, uint& pos) const;
QList<sid_type> readCharset(uint nGlyphs, uint& pos) const;
QList<uint> readEncoding(uint& pos) const;
/// This write the header, name index and TopDict index for exactly one font.
/// All unknown offset are preset as 4 byte cardinal and corresponding operator positions returned
uint writeTopDict(QByteArray name, QMap<operator_type, CFF_Variant> dict, QList<QByteArray> oldStrings, QHash<operator_type, uint>& patches);
/// this will write another segment and return the start offset
uint writeSegment(const QByteArray& data);
void patch(QHash<operator_type, uint> patchPositions, uint patchOffset, operator_type op, uint offset, uint length = 0);
QByteArray makeCharset(QList<sid_type>) const;
QByteArray makeEncoding(QList<uint>) const;
QByteArray makeIndex(QList<QByteArray> data) const;
QByteArray makeDict(QMap<operator_type, CFF_Variant> dict, QList<QByteArray> oldStrings, QHash<operator_type, uint>& patchAddresses);
};
QByteArray extractFace(const QByteArray& cff, int faceIndex);
QByteArray subsetFace(const QByteArray& cff, QList<uint> cids);
}
 
 
#endif /* defined(Scribus__cff_h) */
/trunk/Scribus/scribus/fonts/ftface.cpp
34,7 → 34,7
-2000 broken
>= 0 ok, outline valid
CharMap: unicode -> glyph index
uint[256][256]
gid_type[256][256]
unicode ignores: < 32, ...
unicode emulate: spaces, hyphen, ligatures?, diacritics?
*****/
167,16 → 167,16
}
 
 
uint FtFace::char2CMap(QChar ch) const
ScFace::gid_type FtFace::char2CMap(QChar ch) const
{
// FIXME use cMap cache
FT_Face face = ftFace();
uint gl = FT_Get_Char_Index(face, ch.unicode());
ScFace::gid_type gl = FT_Get_Char_Index(face, ch.unicode());
return gl;
}
 
 
void FtFace::loadGlyph(uint gl) const
void FtFace::loadGlyph(ScFace::gid_type gl) const
{
if (m_glyphWidth.contains(gl))
return;
224,7 → 224,7
}
 
 
qreal FtFace::glyphKerning(uint gl, uint gl2, qreal size) const
qreal FtFace::glyphKerning(ScFace::gid_type gl, ScFace::gid_type gl2, qreal size) const
{
FT_Vector delta;
FT_Face face = ftFace();
250,7 → 250,7
}
 
/*
GlyphMetrics FtFace::glyphBBox (uint gl, qreal sz) const
GlyphMetrics FtFace::glyphBBox (gid_type gl, qreal sz) const
{
FT_Face face = ftFace();
GlyphMetrics result;
307,26 → 307,12
return error;
}
 
QString FtFace::adobeGlyphName(FT_ULong charcode)
QString FtFace::adobeGlyphName(FT_ULong charcode)
{
static const char HEX[] = "0123456789ABCDEF";
QString result;
if (charcode < 0x10000) {
result = QString("uni") + HEX[charcode>>12 & 0xF]
+ HEX[charcode>> 8 & 0xF]
+ HEX[charcode>> 4 & 0xF]
+ HEX[charcode & 0xF];
}
else {
result = QString("u");
for (int i= 28; i >= 0; i-=4) {
if (charcode & (0xF << i))
result += HEX[charcode >> i & 0xF];
}
}
return result;
return ::adobeGlyphName(charcode);
}
 
 
bool FtFace::hasMicrosoftUnicodeCmap(FT_Face face)
{
return (face->charmap && face->charmap->encoding == FT_ENCODING_UNICODE && face->charmap->platform_id == TT_PLATFORM_MICROSOFT);
333,7 → 319,7
}
 
 
bool FtFace::glyphNames(QMap<uint, std::pair<QChar, QString> >& GList) const
bool FtFace::glyphNames(ScFace::FaceEncoding& GList) const
{
char buf[50];
FT_ULong charcode;
357,9 → 343,9
// no valid glyphname except ".notdef" starts with '.'
// qDebug() << "\t" << gindex << " '" << charcode << "' --> '" << (notfound? "notfound" : buf) << "'";
if (notfound || buf[0] == '\0' || buf[0] == '.')
GList.insert(gindex, std::make_pair(QChar(static_cast<uint>(charcode)), adobeGlyphName(charcode)));
GList.insert(gindex, std::make_pair(static_cast<ScFace::ucs4_type>(charcode), adobeGlyphName(charcode)));
else
GList.insert(gindex, std::make_pair(QChar(static_cast<uint>(charcode)), QString(reinterpret_cast<char*>(buf))));
GList.insert(gindex, std::make_pair(static_cast<ScFace::ucs4_type>(charcode), QString(reinterpret_cast<char*>(buf))));
 
charcode = FT_Get_Next_Char(face, charcode, &gindex );
}
378,17 → 364,17
QString glyphname(reinterpret_cast<char*>(buf));
 
charcode = 0;
QMap<uint,std::pair<QChar,QString> >::Iterator gli;
ScFace::FaceEncoding::Iterator gli;
for (gli = GList.begin(); gli != GList.end(); ++gli)
{
if (glyphname == gli.value().second)
{
charcode = gli.value().first.unicode();
charcode = gli.value().first;
break;
}
}
// qDebug() << "\tmore: " << gindex << " '" << charcode << "' --> '" << buf << "'";
GList.insert(gindex, std::make_pair(QChar(static_cast<uint>(charcode)), glyphname));
GList.insert(gindex, std::make_pair(static_cast<ScFace::ucs4_type>(charcode), glyphname));
}
 
return true;
/trunk/Scribus/scribus/fonts/ftface.h
79,20 → 79,20
 
//FIXME QMap<QString,QString> fontDictionary(qreal sz=1.0) const;
 
uint char2CMap(QChar ch) const;
ScFace::gid_type char2CMap(QChar ch) const;
 
qreal glyphKerning (uint gl1, uint gl2, qreal sz) const;
// GlyphMetrics glyphBBox (uint gl, qreal sz) const;
qreal glyphKerning (ScFace::gid_type gl1, ScFace::gid_type gl2, qreal sz) const;
// GlyphMetrics glyphBBox (gid_type gl, qreal sz) const;
 
void RawData (QByteArray & bb) const;
 
static bool hasMicrosoftUnicodeCmap(FT_Face face);
static QString adobeGlyphName(FT_ULong charcode);
virtual bool glyphNames(QMap<uint, std::pair<QChar, QString> >& GList) const;
virtual bool glyphNames(ScFace::FaceEncoding& GList) const;
 
void load () const;
void unload () const;
void loadGlyph (uint ch) const;
void loadGlyph (ScFace::gid_type gl) const;
 
protected:
mutable FT_Face m_face;
/trunk/Scribus/scribus/fonts/scface.cpp
30,13 → 30,13
{
}
 
qreal ScFace::ScFaceData::glyphKerning(uint /*gl1*/, uint /*gl2*/, qreal /*sz*/) const
qreal ScFace::ScFaceData::glyphKerning(gid_type /*gl1*/, gid_type /*gl2*/, qreal /*sz*/) const
{
return 0.0;
}
 
 
bool ScFace::ScFaceData::glyphNames(QMap<uint, std::pair<QChar, QString> >& /*gList*/) const
bool ScFace::ScFaceData::glyphNames(FaceEncoding& /*gList*/) const
{
return false;
}
48,7 → 48,7
}
 
 
GlyphMetrics ScFace::ScFaceData::glyphBBox(uint gl, qreal sz) const
GlyphMetrics ScFace::ScFaceData::glyphBBox(gid_type gl, qreal sz) const
{
GlyphMetrics res;
if (gl == 0 || gl >= CONTROL_GLYPHS)
68,7 → 68,7
}
 
 
qreal ScFace::ScFaceData::glyphWidth(uint gl, qreal size) const
qreal ScFace::ScFaceData::glyphWidth(gid_type gl, qreal size) const
{
if (gl >= CONTROL_GLYPHS)
return 0.0;
81,7 → 81,7
}
 
 
FPointArray ScFace::ScFaceData::glyphOutline(uint gl, qreal sz) const
FPointArray ScFace::ScFaceData::glyphOutline(gid_type gl, qreal sz) const
{
if (gl >= CONTROL_GLYPHS)
return FPointArray();
104,7 → 104,7
}
 
 
FPoint ScFace::ScFaceData::glyphOrigin(uint gl, qreal sz) const
FPoint ScFace::ScFaceData::glyphOrigin(gid_type gl, qreal sz) const
{
if (gl == 0 || gl >= CONTROL_GLYPHS)
return FPoint(0,0);
129,7 → 129,7
-2000 broken
>= 0 ok, outline valid
CharMap: unicode -> glyph index
uint[256][256]
gid_type[256][256]
unicode ignores: < 32, ...
unicode emulate: spaces, hyphen, ligatures?, diacritics?
*****/
341,12 → 341,12
// clear caches
m->m_glyphWidth.clear();
m->m_glyphOutline.clear();
m->m_cMap.clear();
//m->m_cMap.clear();
m->status = ScFace::UNKNOWN;
}
 
 
uint ScFace::emulateGlyph(QChar ch) const
ScFace::gid_type ScFace::emulateGlyph(QChar ch) const
{
if (ch == SpecialChars::LINEBREAK || ch == SpecialChars::PARSEP
|| ch == SpecialChars::FRAMEBREAK || ch == SpecialChars::COLBREAK
362,7 → 362,7
}
 
 
uint ScFace::char2CMap(QChar ch) const
ScFace::gid_type ScFace::char2CMap(QChar ch) const
{
if (m->status == ScFace::UNKNOWN) {
m->load();
371,7 → 371,7
if (ch == SpecialChars::SHYPHEN)
return emulateGlyph(ch);
 
uint gl = m->char2CMap(ch);
gid_type gl = m->char2CMap(ch);
 
if (gl == 0)
return emulateGlyph(ch);
385,7 → 385,7
if (!usable())
return false;
else {
uint gl = char2CMap(ch); // calls load()
gid_type gl = char2CMap(ch); // calls load()
if (gl >= CONTROL_GLYPHS) // those are always empty
return true;
else if (gl != 0) {
406,8 → 406,8
else if (ch.unicode() == 28 || ch.unicode() == 13 || ch.unicode() == 9)
return ch.unicode() == 9 ? 1.0 : 0.0;
else {
uint gl1 = char2CMap(ch);
uint gl2 = char2CMap(ch2);
gid_type gl1 = char2CMap(ch);
gid_type gl2 = char2CMap(ch2);
qreal width = glyphWidth(gl1, size);
if (gl2 != 0)
width += glyphKerning(gl1, gl2, size);
417,7 → 417,7
}
 
 
bool ScFace::EmbedFont(QString &str)
bool ScFace::EmbedFont(QByteArray &str)
{
if (m->status == ScFace::UNKNOWN) {
m->load();
426,7 → 426,7
}
 
 
bool ScFace::glyphNames(QMap<uint, std::pair<QChar, QString> >& gList)
bool ScFace::glyphNames(FaceEncoding& gList)
{
if (m->status == ScFace::UNKNOWN) {
m->load();
451,7 → 451,7
if (m->status != ScFace::LOADED) {
return;
}
for (uint gl=0; gl <= m->maxGlyph; ++gl) {
for (gid_type gl=0; gl <= m->maxGlyph; ++gl) {
if (! m->m_glyphWidth.contains(gl)) {
m->loadGlyph(gl);
m->m_glyphWidth.remove(gl);
/trunk/Scribus/scribus/fonts/scface.h
79,8 → 79,12
// handled by freetype: PFB_MAC, DFONT, HQX, MACBIN,
SFNT, TTCF, UNKNOWN_FORMAT };
 
static const uint CONTROL_GLYPHS = 2000000000; // 2 billion
typedef uint gid_type;
typedef uint ucs4_type;
typedef QMap<gid_type, std::pair<ucs4_type, QString> > FaceEncoding;
 
static const gid_type CONTROL_GLYPHS = 2000000000; // 2 billion
 
struct GlyphData {
FPointArray Outlines;
qreal x;
92,7 → 96,7
GlyphData() : Outlines(), x(0), y(0), bbox_width(1), bbox_ascent(1), bbox_descent(0), broken(true) {}
};
 
 
/// see accessors for ScFace for docs
class ScFaceData {
public:
118,11 → 122,12
bool usable;
bool embedPs;
bool subset;
bool outline;
 
bool isStroked;
bool isFixedPitch;
bool hasGlyphNames;
uint maxGlyph;
gid_type maxGlyph;
 
ScFaceData();
virtual ~ScFaceData() { };
132,9 → 137,9
Status cachedStatus;
 
// caches
mutable QHash<uint, qreal> m_glyphWidth;
mutable QHash<uint, GlyphData> m_glyphOutline;
mutable QHash<uint, uint> m_cMap;
mutable QHash<gid_type, qreal> m_glyphWidth;
mutable QHash<gid_type, GlyphData> m_glyphOutline;
//mutable QHash<gid_type, uint> m_cMap;
 
// fill caches & members
 
142,7 → 147,7
{
m_glyphWidth.clear();
m_glyphOutline.clear();
m_cMap.clear();
//m_cMap.clear();
 
status = qMax(cachedStatus, ScFace::LOADED);
}
151,12 → 156,12
{
m_glyphWidth.clear();
m_glyphOutline.clear();
m_cMap.clear();
//m_cMap.clear();
 
status = ScFace::UNKNOWN;
}
 
virtual void loadGlyph(uint /*gl*/) const {}
virtual void loadGlyph(gid_type /*gl*/) const {}
 
// dummy implementations
virtual qreal ascent(qreal sz) const { return sz; }
173,20 → 178,20
virtual qreal underlinePos(qreal /*sz*/) const { return -1.0; }
virtual qreal strokeWidth(qreal /*sz*/) const { return 0.1; }
virtual qreal maxAdvanceWidth(qreal sz) const { return sz; }
virtual uint char2CMap(QChar /*ch*/) const { return 0; }
virtual qreal glyphKerning(uint gl1, uint gl2, qreal sz) const;
virtual gid_type char2CMap(QChar /*ch*/) const { return 0; }
virtual qreal glyphKerning(gid_type gl1, gid_type gl2, qreal sz) const;
virtual QMap<QString,QString> fontDictionary(qreal sz=1.0) const;
virtual GlyphMetrics glyphBBox(uint gl, qreal sz) const;
virtual bool EmbedFont(QString &/*str*/) const { return false; }
virtual GlyphMetrics glyphBBox(gid_type gl, qreal sz) const;
virtual bool EmbedFont(QByteArray &/*str*/) const { return false; }
virtual void RawData(QByteArray & /*bb*/) const {}
 
virtual bool hasNames() const { return hasGlyphNames; }
virtual bool glyphNames(QMap<uint, std::pair<QChar, QString> >& gList) const;
virtual bool glyphNames(QMap<gid_type, std::pair<ucs4_type, QString> >& gList) const;
 
// these use the cache:
virtual qreal glyphWidth(uint gl, qreal sz) const;
virtual FPointArray glyphOutline(uint gl, qreal sz) const;
virtual FPoint glyphOrigin (uint gl, qreal sz) const;
virtual qreal glyphWidth(gid_type gl, qreal sz) const;
virtual FPointArray glyphOutline(gid_type gl, qreal sz) const;
virtual FPoint glyphOrigin (gid_type gl, qreal sz) const;
 
virtual bool isSymbolic() const { return false; }
};
214,9 → 219,9
bool operator!=(const ScFace& other) const { return ! (*this == other); }
 
 
bool EmbedFont(QString &str);
bool EmbedFont(QByteArray &str);
void RawData(QByteArray & bb);
bool glyphNames(QMap<uint, std::pair<QChar, QString> >& gList);
bool glyphNames(QMap<gid_type, std::pair<ucs4_type, QString> >& gList);
 
/// prevent unloading of face data
void increaseUsage() const;
278,6 → 283,9
/// test if this face can be used in documents
bool usable() const { return m->usable && !isNone(); }
 
/// test if this face should be outlined in documents
bool outline() const { return usable() && m->outline; }
/// test if this face can be embedded in PS/PDF
bool embedPs() const { return m->embedPs && m->status < BROKENGLYPHS; }
 
287,6 → 295,7
void usable(bool flag) { m->usable = flag; }
void embedPs(bool flag) { m->embedPs = flag; }
void subset(bool flag) { m->subset = flag; }
void outline(bool flag) { m->outline = flag; }
 
/// deprecated? tells if the face has PS names
bool hasNames() const { return m->hasNames(); }
301,7 → 310,7
bool isOTF() const { return m->typeCode == OTF; }
 
/// returns the highest glyph index in this face
uint maxGlyph() const { return m->maxGlyph; }
gid_type maxGlyph() const { return m->maxGlyph; }
 
/// returns the font family as seen by Scribus
QString family() const { return m->family; }
342,19 → 351,19
// glyph interface
 
/// returns the glyphs normal advance width at size 'sz'
qreal glyphWidth(uint gl, qreal sz=1.0) const { return m->glyphWidth(gl, sz); }
qreal glyphWidth(gid_type gl, qreal sz=1.0) const { return m->glyphWidth(gl, sz); }
 
/// returns the glyph kerning between 'gl1' and 'gl2' at size 'sz'
qreal glyphKerning(uint gl1, uint gl2, qreal sz=1.0) const { return qMax(gl1,gl2) < CONTROL_GLYPHS ? m->glyphKerning(gl1, gl2, sz) : 0; }
qreal glyphKerning(gid_type gl1, gid_type gl2, qreal sz=1.0) const { return qMax(gl1,gl2) < CONTROL_GLYPHS ? m->glyphKerning(gl1, gl2, sz) : 0; }
 
/// returns the glyphs bounding box at size 'sz', ie. the area where this glyph will produce marks
GlyphMetrics glyphBBox(uint gl, qreal sz=1.0) const { return m->glyphBBox(gl, sz); }
GlyphMetrics glyphBBox(gid_type gl, qreal sz=1.0) const { return m->glyphBBox(gl, sz); }
 
/// returns the glyph's outline as a cubic Bezier path
FPointArray glyphOutline(uint gl, qreal sz=1.0) const { return m->glyphOutline(gl, sz); }
FPointArray glyphOutline(gid_type gl, qreal sz=1.0) const { return m->glyphOutline(gl, sz); }
 
/// returns the glyph's origin FIXME: what's that exactly?
FPoint glyphOrigin(uint gl, qreal sz=1.0) const { return m->glyphOrigin(gl, sz); }
FPoint glyphOrigin(gid_type gl, qreal sz=1.0) const { return m->glyphOrigin(gl, sz); }
 
// char interface
 
362,7 → 371,7
bool canRender(QChar ch) const;
 
/// translate unicode to glyph index
uint char2CMap(QChar ch) const;
gid_type char2CMap(QChar ch) const;
 
/// returns the combined glyph width and kerning for 'ch' if followed by 'ch2'
qreal charWidth(QChar ch, qreal sz=1.0, QChar ch2 = QChar(0)) const;
390,7 → 399,7
 
void initFaceData();
void checkAllGlyphs();
uint emulateGlyph(QChar c) const;
gid_type emulateGlyph(QChar c) const;
};
 
#endif
/trunk/Scribus/scribus/fonts/scface_ps.h
116,7 → 116,7
formatCode = ScFace::PFB;
}
 
virtual bool EmbedFont(QString &str) const
virtual bool EmbedFont(QByteArray &str) const
{
QByteArray bb;
RawData(bb);
202,7 → 202,7
{
formatCode = ScFace::PFA;
}
virtual bool EmbedFont(QString &str) const
virtual bool EmbedFont(QByteArray &str) const
{
QByteArray bb;
RawData(bb);
/trunk/Scribus/scribus/fonts/scface_ttf.cpp
18,485 → 18,9
#include "scconfig.h"
#include "sfnt.h"
 
#include FT_TRUETYPE_IDS_H
 
KernFeature::KernFeature ( FT_Face face )
:m_valid ( true )
{
FontName = QString ( face->family_name ) + " " + QString ( face->style_name ) ;
// qDebug() <<"KF"<<FontName;
// QTime t;
// t.start();
FT_ULong length = 0;
if ( !FT_Load_Sfnt_Table ( face, TTAG_GPOS , 0, NULL, &length ) )
{
// qDebug() <<"\t"<<"GPOS table len"<<length;
if ( length > 32 )
{
GPOSTableRaw.resize ( length );
FT_Load_Sfnt_Table ( face, TTAG_GPOS, 0, reinterpret_cast<FT_Byte*> ( GPOSTableRaw.data() ), &length );
 
makeCoverage();
}
else
m_valid = false;
 
GPOSTableRaw.clear();
// coverages.clear();
}
else
m_valid = false;
 
if (!m_valid)
pairs.clear();
// qDebug() <<"\t"<<m_valid;
// qDebug() <<"\t"<<t.elapsed();
}
 
KernFeature::KernFeature ( const KernFeature & kf )
{
m_valid = kf.m_valid;
if ( m_valid )
pairs = kf.pairs;
}
 
 
KernFeature::~ KernFeature()
{
}
 
double KernFeature::getPairValue ( unsigned int glyph1, unsigned int glyph2 ) const
{
if (!m_valid)
return 0.0;
 
if (pairs.contains(glyph1) &&
pairs[glyph1].contains(glyph2))
{
return pairs[glyph1][glyph2];
}
 
//qDebug()<<"Search in classes";
foreach (const quint16& coverageId, coverages.keys())
{
// for each pairpos table, coverage lists covered _first_ (left) glyph
if (!coverages[coverageId].contains(glyph1))
continue;
 
foreach(const quint16& classDefOffset, classGlyphFirst[coverageId].keys())
{
const ClassDefTable& cdt(classGlyphFirst[coverageId][classDefOffset]);
foreach(const quint16& classIndex, cdt.keys())
{
const QList<quint16>& gl(cdt[classIndex]);
if (!gl.contains(glyph1))
continue;
//qDebug()<<"Found G1"<<glyph1<<"in Class"<<classIndex<<"at pos"<<gl.indexOf(glyph1);
// Now we got the index of the first glyph class, see if glyph2 is in one of the left glyphs classes attached to this subtable.
foreach(const quint16& classDefOffset2, classGlyphSecond[coverageId].keys())
{
const ClassDefTable& cdt2(classGlyphSecond[coverageId][classDefOffset2]);
foreach(const quint16& classIndex2, cdt2.keys())
{
const QList<quint16>& gl2(cdt2[classIndex2]);
if (gl2.contains(glyph2))
{
//qDebug()<<"Found G2"<<glyph2<<"in Class"<<classIndex2<<"at pos"<<gl2.indexOf(glyph2);
 
double v(classValue[coverageId][classIndex][classIndex2]);
// Cache this pair into "pairs" map.
pairs[glyph1][glyph2] = v;
return v;
}
}
}
}
}
}
return 0.0;
}
 
void KernFeature::makeCoverage()
{
if ( GPOSTableRaw.isEmpty() )
return;
 
quint16 FeatureList_Offset= toUint16 ( 6 );
quint16 LookupList_Offset = toUint16 ( 8 );
 
// Find the offsets of the kern feature tables
quint16 FeatureCount = toUint16 ( FeatureList_Offset );
QList<quint16> FeatureKern_Offset;
for ( quint16 FeatureRecord ( 0 ); FeatureRecord < FeatureCount; ++ FeatureRecord )
{
int rawIdx ( FeatureList_Offset + 2 + ( 6 * FeatureRecord ) );
quint32 tag ( FT_MAKE_TAG ( GPOSTableRaw.at ( rawIdx ),
GPOSTableRaw.at ( rawIdx + 1 ),
GPOSTableRaw.at ( rawIdx + 2 ),
GPOSTableRaw.at ( rawIdx + 3 ) ) );
if ( tag == TTAG_kern )
{
FeatureKern_Offset << ( toUint16 ( rawIdx + 4 ) + FeatureList_Offset );
}
}
 
// Extract indices of lookups for feture kern
QList<quint16> LookupListIndex;
foreach ( quint16 kern, FeatureKern_Offset )
{
quint16 LookupCount ( toUint16 ( kern + 2 ) );
for ( int llio ( 0 ) ; llio < LookupCount; ++llio )
{
quint16 Idx ( toUint16 ( kern + 4 + ( llio * 2 ) ) );
if ( !LookupListIndex.contains ( Idx ) )
{
LookupListIndex <<Idx ;
}
}
}
 
 
// Extract offsets of lookup tables for feature kern
QList<quint16> LookupTables;
QList<quint16> PairAdjustmentSubTables;
for ( int i ( 0 ); i < LookupListIndex.count(); ++i )
{
int rawIdx ( LookupList_Offset + 2 + ( LookupListIndex[i] * 2 ) );
quint16 Lookup ( toUint16 ( rawIdx ) + LookupList_Offset );
quint16 SubTableCount ( toUint16 ( Lookup + 4 ) );
for ( int stIdx ( 0 ); stIdx < SubTableCount; ++ stIdx )
{
quint16 SubTable ( toUint16 ( Lookup + 6 + ( 2 * stIdx ) ) + Lookup );
 
// quint16 PosFormat ( toUint16 ( SubTable ) );
quint16 Coverage_Offset ( toUint16 ( SubTable + 2 ) + SubTable );
quint16 CoverageFormat ( toUint16 ( Coverage_Offset ) );
 
if ( 1 == CoverageFormat ) // glyph indices based
{
quint16 GlyphCount ( toUint16 ( Coverage_Offset + 2 ) );
quint16 GlyphID ( Coverage_Offset + 4 );
if (GlyphCount == 0) continue;
 
for ( unsigned int gl ( 0 ); gl < GlyphCount; ++gl )
{
coverages[SubTable] << toUint16 ( GlyphID + ( gl * 2 ) );
}
}
else if ( 2 == CoverageFormat ) // Coverage Format2 => ranges based
{
quint16 RangeCount ( toUint16 ( Coverage_Offset + 2 ) );
if (RangeCount == 0) continue;
 
// int gl_base ( 0 );
for ( int r ( 0 ); r < RangeCount; ++r )
{
quint16 rBase ( Coverage_Offset + 4 + ( r * 6 ) );
quint16 Start ( toUint16 ( rBase ) );
quint16 End ( toUint16 ( rBase + 2 ) );
// quint16 StartCoverageIndex ( toUint16 ( rBase + 4 ) );
// #9842 : for some font such as Gabriola Regular
// the range maybe be specified in reverse order
if (Start <= End)
{
for ( unsigned int gl ( Start ); gl <= End; ++gl )
coverages[SubTable] << gl;
}
else
{
for ( int gl ( Start ); gl >= (int) End; --gl )
coverages[SubTable] << gl;
}
}
}
else
{
// qDebug() <<"Unknow Coverage Format:"<<CoverageFormat;
continue;
}
 
makePairs ( SubTable );
}
 
}
 
 
}
 
 
void KernFeature::makePairs ( quint16 subtableOffset )
{
/*
Lookup Type 2:
Pair Adjustment Positioning Subtable
*/
 
quint16 PosFormat ( toUint16 ( subtableOffset ) );
 
if ( PosFormat == 1 )
{
quint16 ValueFormat1 ( toUint16 ( subtableOffset +4 ) );
quint16 ValueFormat2 ( toUint16 ( subtableOffset +6 ) );
quint16 PairSetCount ( toUint16 ( subtableOffset +8 ) );
if ( ValueFormat1 && ValueFormat2 )
{
for ( int psIdx ( 0 ); psIdx < PairSetCount; ++ psIdx )
{
int oldSecondGlyph = -1;
unsigned int FirstGlyph ( coverages[subtableOffset][psIdx] );
quint16 PairSetOffset ( toUint16 ( subtableOffset +10 + ( 2 * psIdx ) ) + subtableOffset );
quint16 PairValueCount ( toUint16 ( PairSetOffset ) );
quint16 PairValueRecord ( PairSetOffset + 2 );
for ( int pvIdx ( 0 ); pvIdx < PairValueCount; ++pvIdx )
{
quint16 recordBase ( PairValueRecord + ( ( 2 + 2 + 2 ) * pvIdx ) );
quint16 SecondGlyph ( toUint16 ( recordBase ) );
qint16 Value1 ( toInt16 ( recordBase + 2 ) );
// #12475 : Per OpenType spec PairValueRecords must be sorted by SecondGlyph.
// If a kerning pair is duplicated, take only the first one into account
// for now. In the future we may have to ignore the GPOS table in such case.
// (http://partners.adobe.com/public/developer/opentype/index_table_formats2.html)
if (oldSecondGlyph >= SecondGlyph)
continue;
pairs[FirstGlyph][SecondGlyph] = double ( Value1 );
oldSecondGlyph = SecondGlyph;
}
}
}
else if ( ValueFormat1 && ( !ValueFormat2 ) )
{
for ( int psIdx ( 0 ); psIdx < PairSetCount; ++ psIdx )
{
int oldSecondGlyph = -1;
unsigned int FirstGlyph ( coverages[subtableOffset][psIdx] );
quint16 PairSetOffset ( toUint16 ( subtableOffset +10 + ( 2 * psIdx ) ) + subtableOffset );
quint16 PairValueCount ( toUint16 ( PairSetOffset ) );
quint16 PairValueRecord ( PairSetOffset + 2 );
for ( int pvIdx ( 0 ); pvIdx < PairValueCount; ++pvIdx )
{
quint16 recordBase ( PairValueRecord + ( ( 2 + 2 ) * pvIdx ) );
quint16 SecondGlyph ( toUint16 ( recordBase ) );
qint16 Value1 ( toInt16 ( recordBase + 2 ) );
// #12475 : Per OpenType spec PairValueRecords must be sorted by SecondGlyph.
// If a kerning pair is duplicated, take only the first one into account
// for now. In the future we may have to ignore the GPOS table in such case.
// (http://partners.adobe.com/public/developer/opentype/index_table_formats2.html)
if (oldSecondGlyph >= SecondGlyph)
continue;
pairs[FirstGlyph][SecondGlyph] = double ( Value1 );
oldSecondGlyph = SecondGlyph;
}
}
}
else
{
// qDebug() <<"ValueFormat1 is null or both ValueFormat1 and ValueFormat2 are null";
}
}
else if ( PosFormat == 2 ) // class kerning
{
quint16 ValueFormat1 ( toUint16 ( subtableOffset +4 ) );
quint16 ValueFormat2 ( toUint16 ( subtableOffset +6 ) );
quint16 ClassDef1 ( toUint16 ( subtableOffset +8 ) + subtableOffset );
quint16 ClassDef2 ( toUint16 ( subtableOffset +10 ) + subtableOffset );
quint16 Class1Count ( toUint16 ( subtableOffset +12 ) );
quint16 Class2Count ( toUint16 ( subtableOffset +14 ) );
quint16 Class1Record ( subtableOffset +16 );
 
// first extract classses
getClass(true, ClassDef1 , subtableOffset );
getClass(false, ClassDef2 , subtableOffset );
 
if ( ValueFormat1 && ValueFormat2 )
{
for ( quint16 C1 ( 0 );C1 < Class1Count; ++C1 )
{
quint16 Class2Record ( Class1Record + ( C1 * ( 2 * 2 * Class2Count ) ) );
for ( quint16 C2 ( 0 );C2 < Class2Count; ++C2 )
{
qint16 Value1 ( toInt16 ( Class2Record + ( C2 * ( 2 * 2 ) ) ) );
if (Value1 != 0)
{
classValue[subtableOffset][C1][C2] = double ( Value1 );
}
}
}
}
else if ( ValueFormat1 && ( !ValueFormat2 ) )
{
for ( quint16 C1 ( 1 );C1 < Class1Count; ++C1 )
{
quint16 Class2Record ( Class1Record + ( C1 * ( 2 * Class2Count ) ) );
for ( quint16 C2 ( 1 );C2 < Class2Count; ++C2 )
{
qint16 Value1 ( toInt16 ( Class2Record + ( C2 * 2 ) ) );
if (Value1 != 0)
{
classValue[subtableOffset][C1][C2] = double ( Value1 );
}
}
}
}
else
{
// qDebug() <<"ValueFormat1 is null or both ValueFormat1 and ValueFormat2 are null";
}
 
}
else
qDebug() <<"unknown PosFormat"<<PosFormat;
}
 
KernFeature::ClassDefTable KernFeature::getClass ( bool leftGlyph, quint16 classDefOffset, quint16 coverageId )
{
if (leftGlyph)
{
if (classGlyphFirst.contains(coverageId) && classGlyphFirst[coverageId].contains(classDefOffset))
return classGlyphFirst[coverageId][classDefOffset];
}
else
{
if (classGlyphSecond.contains(coverageId) && classGlyphSecond[coverageId].contains(classDefOffset))
return classGlyphSecond[coverageId][classDefOffset];
}
 
ClassDefTable ret;
 
QList<quint16> excludeList;
quint16 ClassFormat ( toUint16 ( classDefOffset ) );
if ( ClassFormat == 1 )
{
quint16 StartGlyph ( toUint16 ( classDefOffset +2 ) );
quint16 GlyphCount ( toUint16 ( classDefOffset +4 ) );
quint16 ClassValueArray ( classDefOffset + 6 );
 
for ( quint16 CV ( 0 );CV < GlyphCount; ++CV )
{
excludeList<<StartGlyph + CV;
ret[ toUint16 ( ClassValueArray + ( CV * 2 ) ) ] << StartGlyph + CV;
}
}
else if ( ClassFormat == 2 )
{
quint16 ClassRangeCount ( toUint16 ( classDefOffset + 2 ) );
quint16 ClassRangeRecord ( classDefOffset + 4 );
for ( int CRR ( 0 ); CRR < ClassRangeCount; ++CRR )
{
quint16 Start ( toUint16 ( ClassRangeRecord + ( CRR * 6 ) ) );
quint16 End ( toUint16 ( ClassRangeRecord + ( CRR * 6 ) + 2 ) );
quint16 Class ( toUint16 ( ClassRangeRecord + ( CRR * 6 ) + 4 ) );
 
if (Start <= End)
{
for ( int gl ( Start ); gl <= (int) End; ++gl )
{
excludeList<< (quint16) gl;
ret[Class] << gl;
}
}
else
{
for ( int gl ( Start ); gl >= (int) End; --gl )
{
excludeList<< (quint16) gl;
ret[Class] << gl;
}
}
}
}
else
qDebug() <<"Unknown Class Table type";
 
// if possible (all glyphs are "classed"), avoid to pass through this slow piece of code.
if (excludeList.count() != coverages[coverageId].count())
{
foreach(const quint16& gidx, coverages[coverageId])
{
if (!excludeList.contains(gidx))
ret[0] << gidx;
}
}
if (leftGlyph)
classGlyphFirst[coverageId][classDefOffset] = ret;
else
classGlyphSecond[coverageId][classDefOffset] = ret;
 
return ret;
}
 
quint16 KernFeature::toUint16 ( quint16 index )
{
if ( ( index + 2 ) > GPOSTableRaw.count() )
{
// qDebug() << "HORROR!" << index << GPOSTableRaw.count() << FontName ;
// Rather no kerning at all than random kerning
// m_valid = false;
return 0;
}
// FIXME I just do not know how it has to be done *properly*
quint8 c1 ( GPOSTableRaw.at ( index ) );
quint8 c2 ( GPOSTableRaw.at ( index + 1 ) );
quint16 ret ( ( c1 << 8 ) | c2 );
return ret;
}
 
qint16 KernFeature::toInt16 ( quint16 index )
{
if ( ( index + 2 ) > GPOSTableRaw.count() )
{
return 0;
}
// FIXME I just do not know how it has to be done *properly*
quint8 c1 ( GPOSTableRaw.at ( index ) );
quint8 c2 ( GPOSTableRaw.at ( index + 1 ) );
qint16 ret ( ( c1 << 8 ) | c2 );
return ret;
}
 
 
namespace {
uint word(QByteArray const & bb, uint pos)
{
const unsigned char * pp = reinterpret_cast<const unsigned char*>(bb.data()) + pos;
return pp[0] << 24 | pp[1] << 16 | pp[2] << 8 | pp[3];
}
void putWord(QByteArray & bb, uint pos, uint val)
{
unsigned char * pp = reinterpret_cast<unsigned char*>(bb.data()) + pos;
*pp++ = (val >> 24) & 0xFF;
*pp++ = (val >> 16) & 0xFF;
*pp++ = (val >> 8) & 0xFF;
*pp++ = (val) & 0xFF;
}
uint word16(QByteArray const & bb, uint pos)
{
const unsigned char * pp = reinterpret_cast<const unsigned char*>(bb.data()) + pos;
return pp[0] << 8 | pp[1];
}
//QString tag(QByteArray const & bb, uint pos)
//{
// char buf[5] = "1234";
// buf[0] = bb.data()[pos];
// buf[1] = bb.data()[pos+1];
// buf[2] = bb.data()[pos+2];
// buf[3] = bb.data()[pos+3];
// return buf;
//}
bool copy(QByteArray & dst, uint to, QByteArray & src, uint from, uint len)
{
if (!dst.data())
return false;
if (!src.data())
return false;
if (to + len > static_cast<uint>(dst.size()))
return false;
if (from + len > static_cast<uint>(src.size()))
return false;
memcpy(dst.data() + to, src.data() + from, len);
return true;
}
} //namespace
 
ScFace_ttf::ScFace_ttf ( QString fam, QString sty, QString alt, QString scname, QString psname, QString path, int face )
: FtFace ( fam, sty, alt, scname, psname, path, face )
{
541,7 → 65,7
FtFace::unload();
}
 
qreal ScFace_ttf::glyphKerning ( uint gl1, uint gl2, qreal sz ) const
qreal ScFace_ttf::glyphKerning ( ScFace::gid_type gl1, ScFace::gid_type gl2, qreal sz ) const
{
if ( kernFeature->isValid() )
return kernFeature->getPairValue ( gl1,gl2 ) / m_uniEM * sz;
558,12 → 82,12
// For those fonts we consequently use Adobe Glyph names whenever possible.
const bool avoidFntNames = (formatCode != ScFace::TYPE42 && typeCode == ScFace::TTF) && hasMicrosoftUnicodeCmap(face);
if (avoidFntNames)
return true; // We use Adobe 'uniXXXX' names in such case
return true; // We use Adobe Glyph List or 'uniXXXX' names in such case
 
return FtFace::hasNames();
}
 
bool ScFace_ttf::glyphNames(QMap<uint, std::pair<QChar, QString> >& GList) const
bool ScFace_ttf::glyphNames(ScFace::FaceEncoding& GList) const
{
FT_ULong charcode;
FT_UInt gindex = 0;
582,7 → 106,7
charcode = FT_Get_First_Char(face, &gindex);
while (gindex != 0)
{
GList.insert(gindex, std::make_pair(QChar(static_cast<uint>(charcode)), adobeGlyphName(charcode)));
GList.insert(gindex, std::make_pair(static_cast<ScFace::ucs4_type>(charcode), adobeGlyphName(charcode)));
charcode = FT_Get_Next_Char(face, charcode, &gindex );
}
 
594,7 → 118,7
QByteArray coll;
FtFace::RawData(coll);
// access table for faceIndex
if (faceIndex >= static_cast<int>(word(coll, 8)))
if (faceIndex >= static_cast<int>(sfnt::word(coll, 8)))
{
bb.resize(0);
return;
601,8 → 125,8
}
static const uint OFFSET_TABLE_LEN = 12;
static const uint TDIR_ENTRY_LEN = 16;
uint faceOffset = word(coll, 12 + 4 * faceIndex);
uint nTables = word16(coll, faceOffset + 4);
uint faceOffset = sfnt::word(coll, 12 + 4 * faceIndex);
uint nTables = sfnt::word16(coll, faceOffset + 4);
sDebug(QObject::tr("extracting face %1 from font %2 (offset=%3, nTables=%4)").arg(faceIndex).arg(fontFile).arg(faceOffset).arg(nTables));
uint headerLength = OFFSET_TABLE_LEN + TDIR_ENTRY_LEN * nTables;
uint tableLengths = 0;
609,7 → 133,7
// sum table lengths incl padding
for (uint i=0; i < nTables; ++i)
{
tableLengths += word(coll, faceOffset + OFFSET_TABLE_LEN + TDIR_ENTRY_LEN * i + 12);
tableLengths += sfnt::word(coll, faceOffset + OFFSET_TABLE_LEN + TDIR_ENTRY_LEN * i + 12);
tableLengths = (tableLengths+3) & ~3;
}
bb.resize(headerLength + tableLengths);
617,21 → 141,24
return;
// write header
// sDebug(QObject::tr("memcpy header: %1 %2 %3").arg(0).arg(faceOffset).arg(headerLength));
if (!copy(bb, 0, coll, faceOffset, headerLength))
if (!sfnt::copy(bb, 0, coll, faceOffset, headerLength))
return;
 
uint pos = headerLength;
for (uint i=0; i < nTables; ++i)
{
uint tableSize = word(coll, faceOffset + OFFSET_TABLE_LEN + TDIR_ENTRY_LEN * i + 12);
uint tableStart = word(coll, faceOffset + OFFSET_TABLE_LEN + TDIR_ENTRY_LEN * i + 8);
// sDebug(QObject::tr("table '%1'").arg(tag(coll, tableStart)));
uint dirEntry = faceOffset + OFFSET_TABLE_LEN + TDIR_ENTRY_LEN * i;
uint tableSize = sfnt::word(coll, dirEntry + 12);
uint tableStart = sfnt::word(coll, dirEntry + 8);
// sDebug(QObject::tr("table '%1' %2. %3 ...").arg(QString(sfnt::tag(coll, dirEntry)))
// .arg(QString::number(sfnt::word16(coll,tableStart),16))
// .arg(QString::number(sfnt::word16(coll,tableStart+2),16)));
// sDebug(QObject::tr("memcpy table: %1 %2 %3").arg(pos).arg(tableStart).arg(tableSize));
if (!copy(bb, pos, coll, tableStart, tableSize)) break;
if (!sfnt::copy(bb, pos, coll, tableStart, tableSize)) break;
// write new offset to table entry
// sDebug(QObject::tr("memcpy offset: %1 %2 %3").arg(OFFSET_TABLE_LEN + TDIR_ENTRY_LEN*i + 8).arg(pos).arg(4));
// buggy: not endian aware: memcpy(bb.data() + OFFSET_TABLE_LEN + TDIR_ENTRY_LEN * i + 8, &pos, 4);
putWord(bb, OFFSET_TABLE_LEN + TDIR_ENTRY_LEN * i + 8, pos);
sfnt::putWord(bb, OFFSET_TABLE_LEN + TDIR_ENTRY_LEN * i + 8, pos);
pos += tableSize;
// pad
while ((pos & 3) != 0)
646,15 → 173,16
}
}
 
bool ScFace_ttf::EmbedFont(QString &str) const
bool ScFace_ttf::EmbedFont(QByteArray &str) const
{
QByteArray bb;
FtFace::RawData(bb);
if (formatCode == ScFace::TYPE42) {
//easy:
QByteArray bb;
FtFace::RawData(bb);
str += bb;
str = bb;
return true;
}
QString tmp4;
QString tmp2 = "";
QString tmp3 = "";
/trunk/Scribus/scribus/fonts/scface_ttf.h
9,75 → 9,9
 
#include "scribusapi.h"
#include "fonts/ftface.h"
#include "fonts/sfnt.h"
 
 
#include FT_TRUETYPE_TABLES_H
#include FT_TRUETYPE_TAGS_H
 
 
/**
An object holding a table of kerning pairs extracted from
a kern feature such as found in a GPOS table
*/
class SCRIBUS_API KernFeature
{
typedef QMap<quint16, QList<quint16> > ClassDefTable; // <Class index (0 to N) , list of glyphs >
 
public:
/**
* Build a ready-to-use kerning pairs table
* @param face a valid FT_Face, It won’t be store by KernFeature
*/
KernFeature ( FT_Face face );
KernFeature ( const KernFeature& kf );
~KernFeature();
 
/**
* Get the kerning value for a pair of glyph indexes.
* @param glyph1 Index of the left glyph in logical order
* @param glyph2 Index of the right glyph in logical order
* @return the unscaled delta to apply to xadvance of the first glyph
*/
double getPairValue ( unsigned int glyph1, unsigned int glyph2 ) const;
 
/**
* The table can have been invalidated if something went wrong at any moment.
* @return True if valid, False otherwise.
*/
bool isValid() const {return m_valid;}
 
private:
bool m_valid;
QByteArray GPOSTableRaw;
QMap<quint16,QList<quint16> > coverages;
mutable QMap<quint16, QMap<quint16, double> > pairs;
QMap< quint16, QMap<quint16, ClassDefTable> > classGlyphFirst; // < subtable offset, map<offset, class definition table> > for first glyph
QMap< quint16, QMap<quint16, ClassDefTable> > classGlyphSecond; // < subtable offset, map<offset, class definition table> > for second glyph
QMap< quint16, QMap<int, QMap<int, double> > > classValue; // < subtable offset, map<class1, map<class2, value> > >
 
void makeCoverage();
void makePairs ( quint16 subtableOffset );
 
ClassDefTable getClass (bool leftGlyph, quint16 classDefOffset, quint16 coverageId );
inline quint16 toUint16 ( quint16 index );
inline qint16 toInt16 ( quint16 index );
 
enum ValueFormat
{
XPlacement = 0x0001,
YPlacement = 0x0002,
XAdvance = 0x0004,
YAdvance = 0x0008,
XPlaDevice =0x0010,
YPlaDevice =0x0020,
XAdvDevice =0x0040,
YAdvDevice =0x0080
};
QString FontName;// for debugging purpose
};
 
 
 
/*
Class ScFace_ttf
Subclass of ScFace, specifically for TrueType fonts.
93,17 → 27,19
void load () const;
void unload () const;
 
bool EmbedFont(QString &str) const;
bool EmbedFont(QByteArray &str) const;
void RawData(QByteArray & bb) const;
 
qreal glyphKerning ( uint gl1, uint gl2, qreal sz ) const;
qreal glyphKerning ( ScFace::gid_type gl1, ScFace::gid_type gl2, qreal sz ) const;
virtual bool glyphNames(QMap<uint, std::pair<QChar, QString> >& GList) const;
virtual bool glyphNames(ScFace::FaceEncoding& GList) const;
virtual bool hasNames() const;
virtual bool isSymbolic() const;
 
private:
mutable KernFeature * kernFeature;
mutable sfnt::PostTable checkPost;
 
};
 
#endif
/trunk/Scribus/scribus/fonts/scfontmetrics.cpp
13,6 → 13,12
#include <QRegExp>
#include <QStringList>
 
#include <ft2build.h>
 
#include FT_FREETYPE_H
#include FT_TRUETYPE_TABLES_H
#include FT_TRUETYPE_IDS_H
 
#include "fpoint.h"
#include "fpointarray.h"
#include "ftface.h"
32,12 → 38,12
static QMap<FT_ULong, QString> adobeGlyphNames;
#if 0
static const char* table[] = {
//#include "glyphnames.txt.q"
NULL};
//#include "glyphlist.txt.q"
0};
#endif
 
// private functions
//static void readAdobeGlyphNames();
static void readAdobeGlyphNames();
//static QString adobeGlyphName(FT_ULong charcode);
static int traceMoveto( FT_Vector *to, FPointArray *composite );
static int traceLineto( FT_Vector *to, FPointArray *composite );
72,9 → 78,13
// Since the above function is only available in FreeType 2.1.10 its replaced by
// the following line, assuming that the default charmap has the index 0
int defaultchmap = 0;
FT_ULong dbgInfo = 0;
FT_Load_Sfnt_Table( face, FT_MAKE_TAG('p','o','s','t'), 0, NULL, &dbgInfo );
qDebug() << "setBestEncoding for " << FT_Get_Postscript_Name(face) << " with " << face->num_glyphs << "glyphs, hasNames=" << FT_HAS_GLYPH_NAMES(face) << ", POST size=" << dbgInfo ;
for(int u = 0; u < face->num_charmaps; u++)
{
FT_CharMap charmap = face->charmaps[u];
qDebug() << "Checking cmap " << u << "(" << charmap->platform_id << "," << charmap->encoding_id << "," << FT_Get_CMap_Language_ID(charmap) << ") format " << FT_Get_CMap_Format(charmap);
if (charmap->encoding == FT_ENCODING_UNICODE)
{
FT_Set_Charmap(face, face->charmaps[u]);
86,7 → 96,7
countUniCode++;
charcode = FT_Get_Next_Char(face, charcode, &gindex);
}
// qDebug() << "found Unicode enc for" << face->family_name << face->style_name << "as map" << chmapUniCode << "with" << countUniCode << "glyphs";
qDebug() << "found Unicode enc for" << face->family_name << face->style_name << "as map" << chmapUniCode << "with" << countUniCode << "glyphs";
}
if (charmap->encoding == FT_ENCODING_ADOBE_CUSTOM)
{
93,12 → 103,12
chmapCustom = u;
foundEncoding = true;
retVal = 1;
// qDebug() << "found Custom enc for" << face->family_name << face->style_name;
qDebug() << "found Custom enc for" << face->family_name << face->style_name;
break;
}
else if (charmap->encoding == FT_ENCODING_MS_SYMBOL)
{
// qDebug() << "found Symbol enc for" << face->family_name << face->style_name;
qDebug() << "found Symbol enc for" << face->family_name << face->style_name;
 
chmapCustom = u;
foundEncoding = true;
109,18 → 119,18
int mapToSet = defaultchmap;
if (chmapUniCode >= 0 && countUniCode >= face->num_glyphs-1)
{
// qDebug() << "using Unicode enc for" << face->family_name << face->style_name;
qDebug() << "using Unicode enc for" << face->family_name << face->style_name;
mapToSet = chmapUniCode;
retVal = 0;
}
else if (foundEncoding)
{
// qDebug() << "using special enc for" << face->family_name << face->style_name;
qDebug() << "using special enc for" << face->family_name << face->style_name;
mapToSet = chmapCustom;
}
else
{
// qDebug() << "using default enc for" << face->family_name << face->style_name;
qDebug() << "using default enc for" << face->family_name << face->style_name;
mapToSet = defaultchmap;
retVal = 0;
}
160,7 → 170,7
return retVal;
}
 
FPointArray traceGlyph(FT_Face face, FT_UInt glyphIndex, int chs, qreal *x, qreal *y, bool *err)
FPointArray traceGlyph(FT_Face face, ScFace::gid_type glyphIndex, int chs, qreal *x, qreal *y, bool *err)
{
bool error = false;
//AV: not threadsave, but tracechar is only used in ReadMetrics() and fontSample()
205,7 → 215,7
}
 
 
FPointArray traceChar(FT_Face face, uint chr, int chs, qreal *x, qreal *y, bool *err)
FPointArray traceChar(FT_Face face, ScFace::ucs4_type chr, int chs, qreal *x, qreal *y, bool *err)
{
bool error = false;
FT_UInt glyphIndex;
257,7 → 267,7
// p->drawRect(0.0, 0.0, static_cast<qreal>(w), static_cast<qreal>(h));
p->setBrush(Qt::black);
FPointArray gly;
uint dv;
ScFace::ucs4_type dv;
dv = ts[0].unicode();
error = false;
gly = traceChar(face, dv, s, &x, &y, &error);
317,7 → 327,7
}
 
#if 0
bool GlyphNames(const FtFace& fnt, QMap<uint, std::pair<QChar, QString> >& GList)
bool GlyphNames(const FtFace& fnt, FaceEncoding& GList)
{
char buf[50];
FT_ULong charcode;
333,7 → 343,8
// The glyph name table embedded in Truetype fonts is not reliable.
// For those fonts we consequently use Adobe Glyph names whenever possible.
const bool avoidFntNames = (fnt.formatCode != ScFace::TYPE42 && fnt.typeCode == ScFace::TTF) &&
(face->charmap && face->charmap->encoding == FT_ENCODING_UNICODE);
(face->charmap && face->charmap->encoding == FT_ENCODING_UNICODE && face->charmap->platform_id == TT_PLATFORM_MICROSOFT);
const bool hasPSNames = FT_HAS_GLYPH_NAMES(face);
// qDebug() << "reading metrics for" << face->family_name << face->style_name;
348,9 → 359,9
// no valid glyphname except ".notdef" starts with '.'
// qDebug() << "\t" << gindex << " '" << charcode << "' --> '" << (notfound? "notfound" : buf) << "'";
if (notfound || buf[0] == '\0' || buf[0] == '.')
GList.insert(gindex, std::make_pair(QChar(static_cast<uint>(charcode)), adobeGlyphName(charcode)));
GList.insert(gindex, std::make_pair(static_cast<ucs4_type>(charcode), adobeGlyphName(charcode)));
else
GList.insert(gindex, std::make_pair(QChar(static_cast<uint>(charcode)), QString(reinterpret_cast<char*>(buf))));
GList.insert(gindex, std::make_pair(static_cast<ucs4_type>(charcode), QString(reinterpret_cast<char*>(buf))));
 
charcode = FT_Get_Next_Char(face, charcode, &gindex );
}
369,7 → 380,7
QString glyphname(reinterpret_cast<char*>(buf));
 
charcode = 0;
QMap<uint,std::pair<QChar,QString> >::Iterator gli;
faceEncoding::Iterator gli;
for (gli = GList.begin(); gli != GList.end(); ++gli)
{
if (glyphname == gli.value().second)
381,7 → 392,7
// qDebug() << "\tmore: " << gindex << " '" << charcode << "' --> '" << buf << "'";
if (avoidFntNames && buf[0] != '.' && buf[0] != '\0')
glyphname = adobeGlyphName(charcode);
GList.insert(gindex, std::make_pair(QChar(static_cast<uint>(charcode)), glyphname));
GList.insert(gindex, std::make_pair(static_cast<ucs4_type>(charcode), glyphname));
}
 
return true;
461,6 → 472,8
/// if in AGL, use that name, else use "uni1234" or "u12345"
QString adobeGlyphName(FT_ULong charcode)
{
if (adobeGlyphNames.empty())
readAdobeGlyphNames();
static const char HEX[] = "0123456789ABCDEF";
QString result;
if (adobeGlyphNames.contains(charcode))
488,8 → 501,8
qreal width;
FT_Vector delta;
FT_Face face;
uint c1 = ch.at(0).unicode();
uint c2 = ch2.at(0).unicode();
ucs4_type c1 = ch.at(0).unicode();
ucs4_type c2 = ch2.at(0).unicode();
qreal size10=Size/10.0;
if (scFace->canRender(ch[0]))
{
501,8 → 514,8
****\/
if (true || FT_HAS_KERNING(face) )
{
uint cl = FT_Get_Char_Index(face, c1);
uint cr = FT_Get_Char_Index(face, c2);
gid_type cl = FT_Get_Char_Index(face, c1);
gid_type cr = FT_Get_Char_Index(face, c2);
FT_Error error = FT_Get_Kerning(face, cl, cr, FT_KERNING_UNSCALED, &delta);
if (error) {
qDebug() << QString("Error %2 when accessing kerning pair for font %1").arg(scFace->scName()).arg(error);
524,12 → 537,12
qreal RealCWidth(ScribusDoc *, ScFace* scFace, QString ch, int Size)
{
qreal w, ww;
uint c1 = ch.at(0).unicode();
ucs4_type c1 = ch.at(0).unicode();
FT_Face face;
if (scFace->canRender(ch.at(0)))
{
face = scFace->ftFace();
uint cl = FT_Get_Char_Index(face, c1);
gid_type cl = FT_Get_Char_Index(face, c1);
int error = FT_Load_Glyph(face, cl, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP );
if (!error) {
qreal uniEM = static_cast<qreal>(face->units_per_EM);
547,12 → 560,12
qreal RealCHeight(ScribusDoc *, ScFace* scFace, QString ch, int Size)
{
qreal w;
uint c1 = ch.at(0).unicode();
ucs4_type c1 = ch.at(0).unicode();
FT_Face face;
if (scFace->canRender(ch.at(0)))
{
face = scFace->ftFace();
uint cl = FT_Get_Char_Index(face, c1);
gid_type cl = FT_Get_Char_Index(face, c1);
int error = FT_Load_Glyph(face, cl, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP );
if (!error) {
qreal uniEM = static_cast<qreal>(face->units_per_EM);
571,12 → 584,12
qreal RealCAscent(ScribusDoc *, ScFace* scFace, QString ch, int Size)
{
qreal w;
uint c1 = ch.at(0).unicode();
ucs4_type c1 = ch.at(0).unicode();
FT_Face face;
if (scFace->canRender(ch.at(0)))
{
face = scFace->ftFace();
uint cl = FT_Get_Char_Index(face, c1);
gid_type cl = FT_Get_Char_Index(face, c1);
int error = FT_Load_Glyph(face, cl, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP );
if (! error) {
qreal uniEM = static_cast<qreal>(face->units_per_EM);
/trunk/Scribus/scribus/fonts/scfontmetrics.h
29,9 → 29,11
struct FtFace;
 
int SCRIBUS_API setBestEncoding(FT_Face face);
FPointArray SCRIBUS_API traceChar(FT_Face face, uint chr, int chs, qreal *x, qreal *y, bool *err);
FPointArray SCRIBUS_API traceGlyph(FT_Face face, uint chr, int chs, qreal *x, qreal *y, bool *err);
QString adobeGlyphName(FT_ULong charcode);
 
FPointArray SCRIBUS_API traceChar(FT_Face face, ScFace::ucs4_type chr, int chs, qreal *x, qreal *y, bool *err);
FPointArray SCRIBUS_API traceGlyph(FT_Face face, ScFace::gid_type gl, int chs, qreal *x, qreal *y, bool *err);
QPixmap SCRIBUS_API FontSample(const ScFace& fnt, int s, QString ts, QColor back, bool force = false);
//bool SCRIBUS_API GlyphNames(const FtFace& fnt, QMap<uint, std::pair<QChar, QString> >& GList);
//bool SCRIBUS_API GlyphNames(const FtFace& fnt, ScFace::FaceEncoding& GList);
 
#endif
/trunk/Scribus/scribus/fonts/sfnt.cpp
7,6 → 7,7
//
 
#include "fonts/sfnt.h"
#include "fonts/sfnt_format.h"
 
#include FT_TRUETYPE_TABLES_H
#include FT_TRUETYPE_TAGS_H
16,19 → 17,18
 
namespace sfnt {
uchar byte(QByteArray const & bb, uint pos)
uchar byte(const QByteArray & bb, uint pos)
{
const unsigned char * pp = reinterpret_cast<const unsigned char*>(bb.data()) + pos;
return pp[0];
}
uint word(QByteArray const & bb, uint pos)
quint32 word(const QByteArray & bb, uint pos)
{
const unsigned char * pp = reinterpret_cast<const unsigned char*>(bb.data()) + pos;
return pp[0] << 24 | pp[1] << 16 | pp[2] << 8 | pp[3];
}
void putWord(QByteArray & bb, uint pos, uint val)
void putWord(QByteArray & bb, uint pos, quint32 val)
{
unsigned char * pp = reinterpret_cast<unsigned char*>(bb.data()) + pos;
*pp++ = (val >> 24) & 0xFF;
37,24 → 37,51
*pp++ = (val) & 0xFF;
}
uint word16(QByteArray const & bb, uint pos)
void appendWord(QByteArray & bb, quint32 val)
{
uint pos = bb.size();
bb.resize(pos + 4);
putWord(bb, pos, val);
}
quint16 word16(const QByteArray & bb, uint pos)
{
const unsigned char * pp = reinterpret_cast<const unsigned char*>(bb.data()) + pos;
return pp[0] << 8 | pp[1];
}
QByteArray tag(QByteArray const & bb, uint pos)
void putWord16(QByteArray & bb, uint pos, quint16 val)
{
char buf[5] = "1234";
buf[0] = bb.data()[pos];
buf[1] = bb.data()[pos+1];
buf[2] = bb.data()[pos+2];
buf[3] = bb.data()[pos+3];
return buf;
unsigned char * pp = reinterpret_cast<unsigned char*>(bb.data()) + pos;
*pp++ = (val >> 8) & 0xFF;
*pp++ = (val) & 0xFF;
}
bool copy(QByteArray & dst, uint to, QByteArray & src, uint from, uint len)
void appendWord16(QByteArray & bb, quint16 val)
{
uint pos = bb.size();
bb.resize(pos + 2);
putWord16(bb, pos, val);
}
const QByteArray tag(const QByteArray& bb, uint pos)
{
return QByteArray::fromRawData(bb.constData() + pos, 4);
}
const QByteArray tag(uint word)
{
QByteArray result;
result.resize(4);
result[0] = (word >> 24) & 0xFF;
result[1] = (word >> 16) & 0xFF;
result[2] = (word >> 8) & 0xFF;
result[3] = (word) & 0xFF;
return result;
}
bool copy(QByteArray & dst, uint to, const QByteArray & src, uint from, uint len)
{
if (!dst.data())
return false;
if (!src.data())
70,14 → 97,8
 
 
enum post_format {
post_format10 = 0x00010000,
post_format20 = 0x00020000,
post_format25 = 0x00025000,
post_format30 = 0x00030000,
post_format40 = 0x00040000,
};
 
 
const uint post_format10_names_count = 258;
 
286,12 → 307,12
 
 
 
int PostTable::numberOfGlyphs() const
uint PostTable::numberOfGlyphs() const
{
if (names.length() > 0)
return names.length();
else
return sfnt::post_format10_names_count;
return post_format10_names_count;
}
 
QString PostTable::nameFor(uint glyph) const
302,7 → 323,7
}
else if (glyph < sfnt::post_format10_names_count)
{
return sfnt::post_format10_names[glyph];
return post_format10_names[glyph];
}
else
{
316,7 → 337,7
QByteArray postData;
FT_ULong size = 0;
int error = FT_Load_Sfnt_Table ( face, TTAG_post , 0, NULL, &size );
// qDebug() << TTAG_post << error << size;
qDebug() << "load post" << error << size;
if (error || size == 0)
{
errorMsg = "no post table";
332,7 → 353,7
return;
}
switch (sfnt::word(postData, 0))
switch (sfnt::word(postData, ttf_post_format))
{
case sfnt::post_format10:
usable = true;
344,11 → 365,10
errorMsg = QString("post table has no glyph names");
usable = false;
return;
case sfnt::post_format25:
case sfnt::post_format40:
default:
errorMsg = QString("unsupported post format %1").arg(QString::number(sfnt::word(postData,0),16));
errorMsg = QString("unsupported post format %1").arg(sfnt::word(postData,0));
usable = false;
return;
356,44 → 376,36
QMap<QString,uint> usedNames;
QList<QByteArray> pascalStrings;
const uint post_header_length = 32;
uint nrOfGlyphs = sfnt::word16(postData, post_header_length);
uint stringPos = post_header_length + 2 + 2 * nrOfGlyphs;
// qDebug() << "#glyphs" << nrOfGlyphs << "start strings" << stringPos;
while (stringPos < postData.size())
uint nrOfGlyphs = sfnt::word16(postData, ttf_post_header_length);
uint stringPos = ttf_post_header_length + 2 + 2 * nrOfGlyphs;
while (stringPos < postData.length())
{
int strLen = sfnt::byte(postData,stringPos);
int strLen = byte(postData, stringPos);
++stringPos;
pascalStrings.append(postData.mid(stringPos, strLen));
stringPos += strLen;
// qDebug() << pascalStrings.length() << pascalStrings[pascalStrings.length()-1];
 
}
uint pos = post_header_length + 2;
uint pos = ttf_post_header_length + 2;
for (int gid = 0; gid < nrOfGlyphs; ++gid)
{
uint nameIndex = sfnt::word16(postData, pos);
pos += 2;
QString name;
// qDebug() << "looking up name index " << nameIndex;
if (nameIndex < sfnt::post_format10_names_count) {
if (nameIndex < sfnt::post_format10_names_count)
name = sfnt::post_format10_names[nameIndex];
}
else if (nameIndex < pascalStrings.length() + sfnt::post_format10_names_count) {
else if (nameIndex < pascalStrings.length() + sfnt::post_format10_names_count)
name = pascalStrings[nameIndex - sfnt::post_format10_names_count];
}
else {
else {
usable = false;
errorMsg = QString("missing name %1 for glyph %2").arg(nameIndex).arg(gid);
errorMsg = QString("missing name %1 for glyph %2").arg(nameIndex).arg(gid);
return;
}
if (name != ".notdef" && usedNames.contains(name))
if (name != ".notdef" && name[0] != QChar(0) && usedNames.contains(name))
{
usable = false;
errorMsg = QString("duplicate name %1 used for glyphs %2 and %3").arg(name).arg(gid).arg(usedNames[name]);
return;
}
// qDebug() << name << "=" << gid;
usedNames[name] = gid;
names.append(name);
}
401,6 → 413,1230
usable = true;
}
 
int copyTable(QByteArray& ttf, uint destDirEntry, uint pos, const QByteArray& source, uint dirEntry)
{
FT_ULong tag = word(source, dirEntry + ttf_TableRecord_tag);
uint checksum = word(source, dirEntry + ttf_TableRecord_checkSum);
uint tableStart = word(source, dirEntry + ttf_TableRecord_offset);
uint tableSize = word(source, dirEntry + ttf_TableRecord_length);
if (!copy(ttf, pos, source, tableStart, tableSize))
return -1;
putWord(ttf, destDirEntry + ttf_TableRecord_tag, tag);
putWord(ttf, destDirEntry + ttf_TableRecord_checkSum, checksum);
putWord(ttf, destDirEntry + ttf_TableRecord_offset, pos);
putWord(ttf, destDirEntry + ttf_TableRecord_length, tableSize);
return tableSize;
}
 
QByteArray extractFace(const QByteArray& coll, int faceIndex)
{
QByteArray result;
const int numFonts = word(coll, ttc_numFonts);
if (faceIndex >= static_cast<int>(numFonts))
{
return result;
}
 
uint faceOffset = sfnt::word(coll, ttc_OffsetTables + 4 * faceIndex);
uint nTables = sfnt::word16(coll, faceOffset + ttf_numtables);
qDebug() << QObject::tr("extracting face %1 from font %2 (offset=%3, nTables=%4)").arg(faceIndex).arg("collection").arg(faceOffset).arg(nTables);
uint headerLength = ttf_TableRecords + ttf_TableRecord_Size * nTables;
uint tableLengths = 0;
// sum table lengths incl padding
for (uint i=0; i < nTables; ++i)
{
tableLengths += sfnt::word(coll, faceOffset + ttf_TableRecords + ttf_TableRecord_Size * i + ttf_TableRecord_length);
tableLengths = (tableLengths+3) & ~3;
}
result.resize(headerLength + tableLengths);
if (!result.data())
{
result.resize(0);
return result;
}
// write header
// sDebug(QObject::tr("memcpy header: %1 %2 %3").arg(0).arg(faceOffset).arg(headerLength));
if (!copy(result, 0, coll, faceOffset, headerLength))
{
result.resize(0);
return result;
}
uint pos = headerLength;
for (uint i=0; i < nTables; ++i)
{
uint sourceDirEntry = faceOffset + ttf_TableRecords + ttf_TableRecord_Size * i;
uint destDirEntry = ttf_TableRecords + ttf_TableRecord_Size * i;
int tableSize = copyTable(result, destDirEntry, pos, coll, sourceDirEntry);
if (tableSize < 0)
{
result.resize(0);
return result;
}
pos += tableSize;
 
// pad
while ((pos & 3) != 0)
result.data()[pos++] = '\0';
}
return result;
}
uint getTableDirEntry(const QByteArray& ttf, const QByteArray& ttfTag)
{
uint nTables = word16(ttf, ttf_numtables);
uint pos = ttf_TableRecords;
for (uint i=0; i < nTables; ++i)
{
if (ttfTag == tag(word(ttf, pos + ttf_TableRecord_tag)))
{
return pos;
}
pos += ttf_TableRecord_Size;
}
return 0;
}
const QByteArray getTable(const QByteArray& ttf, const QByteArray& ttfTag)
{
uint pos = getTableDirEntry(ttf, ttfTag);
if (pos > 0)
{
uint offset = word(ttf, pos + ttf_TableRecord_offset);
uint length = word(ttf, pos + ttf_TableRecord_length);
return QByteArray::fromRawData(ttf.constData() + offset, length);
}
else
{
return QByteArray();
}
}
QByteArray createTableDir(const QList<QByteArray>& tags)
{
QByteArray result;
uint numTables = tags.length();
uint tableRecordsSize = numTables * ttf_TableRecord_Size;
result.resize(ttf_TableRecords + tableRecordsSize);
uint entrySelector = 0;
uint searchRange = ttf_TableRecord_Size;
while (2*searchRange < tableRecordsSize)
{
searchRange *= 2;
++entrySelector;
}
uint rangeShift = tableRecordsSize - searchRange;
putWord(result, ttf_sfnt_version, 0x00010000);
putWord16(result, ttf_numtables, numTables);
putWord16(result, ttf_searchRange, searchRange);
putWord16(result, ttf_entrySelector, entrySelector);
putWord16(result, ttf_rangeShift, rangeShift);
for (int i = 0; i < numTables; ++i)
{
copy(result, ttf_TableRecords + i*ttf_TableRecord_Size + ttf_TableRecord_tag, tags[i], 0, 4);
putWord(result, ttf_TableRecords + i*ttf_TableRecord_Size + ttf_TableRecord_checkSum, 0);
putWord(result, ttf_TableRecords + i*ttf_TableRecord_Size + ttf_TableRecord_offset, 0);
putWord(result, ttf_TableRecords + i*ttf_TableRecord_Size +ttf_TableRecord_length, 0);
}
return result;
}
quint32 calcTableChecksum(QByteArray& table)
{
quint32 Sum = 0L;
for (int pos = 0; pos < table.length(); pos += 4)
Sum += word(table, pos);
return Sum;
}
 
void writeTable(QByteArray& ttf, const QByteArray& tag, QByteArray& table)
{
qDebug() << "writing table" << tag << table.size() << "@" << ttf.size();
uint length = table.size();
while (table.size() & 0x3)
table.append('\0');
uint offset = ttf.size();
uint checksum = calcTableChecksum(table);
uint pos = getTableDirEntry(ttf, tag);
putWord(ttf, pos + ttf_TableRecord_checkSum, checksum);
putWord(ttf, pos + ttf_TableRecord_offset, offset);
putWord(ttf, pos + ttf_TableRecord_length, length);
ttf.append(table);
}
bool hasLongLocaFormat(const QByteArray& ttf)
{
const QByteArray head = getTable(ttf, "head");
uint idxToLocFormat = word16(head, ttf_head_indexToLocFormat);
qDebug() << "loca format:" << (void*)idxToLocFormat;
return idxToLocFormat == 1;
}
QList<quint32> readLoca(const QByteArray& ttf)
{
QList<quint32> result;
const QByteArray loca = getTable(ttf, "loca");
if (hasLongLocaFormat(ttf))
{
for (int i = 0; i < loca.length(); i+=4)
{
result.append(word(loca, i));
}
}
else
{
for (int i = 0; i < loca.length(); i+=2)
{
result.append(word16(loca, i) * 2);
}
}
return result;
}
const QByteArray writeLoca(const QList<uint>& loca, bool longFormat)
{
QByteArray result;
if (longFormat)
{
for(int i=0; i < loca.length(); ++i)
appendWord(result, loca[i]);
}
else
{
for(int i=0; i < loca.length(); ++i)
appendWord16(result, loca[i] / 2);
}
return result;
}
QList<std::pair<qint16,quint16> > readHmtx(const QByteArray& ttf)
{
QList<std::pair<qint16,quint16> > result;
const QByteArray hhea = getTable(ttf, "hhea");
const QByteArray hmtx = getTable(ttf, "hmtx");
uint endOfLongHorMetrics = 4 * word16(hhea, ttf_hhea_numOfLongHorMetrics);
qint16 advance;
quint16 leftSideBearing;
uint pos = 0;
while (pos < endOfLongHorMetrics)
{
advance = word16(hmtx, pos);
leftSideBearing = word16(hmtx, pos+2);
qDebug() << pos << "hmtx" << advance << leftSideBearing;
result.append(std::pair<qint16,quint16>(advance, leftSideBearing));
pos += 4;
}
while (pos < hmtx.length())
{
leftSideBearing = word16(hmtx, pos);
qDebug() << pos << "hmtx =" << advance << leftSideBearing;
result.append(std::pair<qint16,quint16>(advance, leftSideBearing));
pos += 2;
}
return result;
}
QByteArray writeHmtx(const QList<std::pair<qint16,quint16> >& longHorMetrics)
{
QByteArray result;
QList<std::pair<qint16,quint16> >::const_iterator it;
int i = 0;
for (it = longHorMetrics.cbegin(); it < longHorMetrics.cend(); ++it)
{
qDebug() << "hmtx" << i++ << it->first << it->second;
appendWord16(result, it->first);
appendWord16(result, it->second);
}
return result;
}
QMap<uint, uint> readCMap(const QByteArray& ttf)
{
QMap<uint,uint> result;
const QByteArray cmaps = getTable(ttf, "cmap");
uint numSubtables = word16(cmaps, ttf_cmap_numberSubtables);
uint startOfUnicodeTable = 0;
uint format = 0;
uint pos = ttf_cmap_encodings;
for (int i = 0; i < numSubtables; ++i)
{
uint platform = word16(cmaps, pos + ttf_cmap_encoding_platformID);
uint encoding = word16(cmaps, pos + ttf_cmap_encoding_platformSpecificID);
uint offset = word(cmaps, pos + ttf_cmap_encoding_offset);
format = word16(cmaps, offset + ttf_cmapx_format);
pos += ttf_cmap_encoding_Size;
if (format < 4 || format > 12)
continue;
if (platform == 0 || (platform == 3 && encoding == 1))
{
startOfUnicodeTable = offset;
break;
}
format = 1; // no such format
}
qDebug() << "reading cmap format" << format;
switch(format)
{
case 4:
{
uint segCount2 = word16(cmaps, startOfUnicodeTable + ttf_cmap4_segCountX2);
uint endCodes = startOfUnicodeTable + ttf_cmap4_EndCodes;
uint startCodes = endCodes + segCount2 + ttf_cmap4_StartCodes_EndCodes;
uint idDeltas = startCodes + segCount2 + ttf_cmap4_IdDeltas_StartCodes;
uint idRangeOffsets = idDeltas + segCount2 + ttf_cmap4_IdRangeOffsets_IdDeltas;
//uint glyphIndexArray = idRangeOffsets + segCount2 + ttf_cmap4_GlyphIds_IdRangeOffsets;
for (int seg = 0; seg < segCount2; seg+=2)
{
uint start = word16(cmaps, startCodes + seg);
uint end = word16(cmaps, endCodes + seg);
uint idDelta = word16(cmaps, idDeltas + seg);
uint idRangeOffset = word16(cmaps, idRangeOffsets + seg);
for (uint c = start; c <= end; ++c)
{
quint16 glyph;
if (idRangeOffset > 0)
{
uint glyphIndexAdress = idRangeOffset + 2*(c-start) + (idRangeOffsets + seg);
glyph = word16(cmaps, glyphIndexAdress);
if (glyph != 0)
glyph += idDelta;
}
else
{
glyph = c + idDelta;
}
if (!result.contains(c))
{
// search would always find the one in the segment with the lower endcode, i.e. earlier segment
if (c < 256 || glyph == 0) qDebug() << "(" << QChar(c) << "," << glyph << ")";
result[c] = glyph;
}
else
{
// nothing to do. No idea if fonts with overlapping cmap4 segments exist, though.
}
}
}
break;
}
case 6:
{
uint firstCode = word16(cmaps, startOfUnicodeTable + ttf_cmap6_firstCode);
uint count = word16(cmaps, startOfUnicodeTable + ttf_cmap6_entryCount);
pos = word16(cmaps, startOfUnicodeTable + ttf_cmap6_glyphIndexArray);
for (int i = 0; i < count; ++i)
{
result[firstCode + i] = word16(cmaps, pos);
pos += 2;
}
break;
}
case 12:
{
uint nGroups = word(cmaps, startOfUnicodeTable + ttf_cmap12_nGroups);
pos = startOfUnicodeTable + ttf_cmap12_Groups;
for (int grp = 0; grp < nGroups; ++grp)
{
uint start = word(cmaps, pos + ttf_cmap12_Group_startCharCode);
uint end = word(cmaps, pos + ttf_cmap12_Group_endCharCode);
uint gid = word(cmaps, pos + ttf_cmap12_Group_startGlyphCode);
for (uint c = start; c <= end; ++c)
{
result[c] = gid;
++gid;
}
pos += ttf_cmap12_Group_Size;
}
break;
}
default:
{
qDebug() << "unsupported cmap format" << format;
break;
}
}
return result;
}
const QByteArray writeCMap(const QMap<uint, uint>& cmap)
{
// we always write only one table: platform=3(MS), encoding=1(Unicode 16bit)
QByteArray result;
appendWord16(result, 0); // version
appendWord16(result, 1); // number of subtables
appendWord16(result, 3); // platformID Microsoft
appendWord16(result, 1); // encodingID UnicodeBMP
appendWord(result, result.size() + 4); // offset
// find the segments
QList<uint> chars;
QMap<uint, uint>::ConstIterator cit;
qDebug() << "writing cmap";
for(cit = cmap.cbegin(); cit != cmap.cend(); ++cit)
{
uint ch = cit.key();
if (!QChar::requiresSurrogates(ch) && cit.value() != 0)
{
qDebug() << "(" << QChar(cit.key()) << "," << cit.value() << ")";
chars.append(ch);
}
// qDebug() << QChar(ch) << QChar::requiresSurrogates(ch) << cit.value();
}
std::sort(chars.begin(), chars.end());
QList<quint16> startCodes;
QList<quint16> endCodes;
QList<quint16> idDeltas;
QList<quint16> rangeOffsets;
uint pos = 0;
do {
quint16 start = chars[pos];
quint16 delta = cmap[start] - start;
quint16 rangeOffset = 0;
quint16 end = start;
quint16 next;
++pos;
while (pos < chars.length() && (next = chars[pos]) == end+1)
{
end = next;
if (delta != (quint16)(cmap[chars[pos]] - next))
{
rangeOffset = 1; // will be changed later
}
++pos;
}
startCodes.append(start);
endCodes.append(end);
idDeltas.append(delta);
rangeOffsets.append(rangeOffset);
} while(pos < chars.length());
startCodes.append(0xFFFF);
endCodes.append(0xFFFF);
idDeltas.append(1); // makes gid 0
rangeOffsets.append(0);
// write the tables
uint startOfTable = result.size();
result.resize(startOfTable + ttf_cmap4_EndCodes);
 
uint segCount = endCodes.length();
uint segCountX2 = 2 * segCount;
uint entrySelector = 0;
uint searchRange = 2;
while (searchRange <= segCount)
{
++entrySelector;
searchRange *= 2;
}
putWord16(result, startOfTable + ttf_cmapx_format, 4);
/* ttf_cmap4_length is set later */
putWord16(result, startOfTable + ttf_cmap4_language, 0);
putWord16(result, startOfTable + ttf_cmap4_segCountX2, segCountX2);
putWord16(result, startOfTable + ttf_cmap4_searchRange, searchRange);
putWord16(result, startOfTable + ttf_cmap4_entrySelector, entrySelector);
putWord16(result, startOfTable + ttf_cmap4_rangeShift, segCountX2 - searchRange);
for (int i = 0; i < segCount; ++i)
{
appendWord16(result, endCodes[i]);
};
appendWord16(result, 0); // reservedPad
for (int i = 0; i < segCount; ++i)
{
appendWord16(result, startCodes[i]);
};
for (int i = 0; i < segCount; ++i)
{
appendWord16(result, idDeltas[i]);
};
uint startOfIdRangeOffsetTable = result.size();
uint startOfGlyphIndexArray = startOfIdRangeOffsetTable + segCountX2;
result.resize(startOfGlyphIndexArray);
for (int i = 0; i < segCount; ++i)
{
uint idRangeOffsetAddress = startOfIdRangeOffsetTable + 2*i;
if (rangeOffsets[i] == 0)
{
quint16 dbg = startCodes[i] + idDeltas[i];
qDebug() << QChar(startCodes[i]) << "-" << QChar(endCodes[i]) << "/" << (endCodes[i]-startCodes[i]+1) << "+" << idDeltas[i] << "-->" << dbg;
putWord16(result, idRangeOffsetAddress, 0);
}
else
{
quint16 idRangeOffset = result.size() - idRangeOffsetAddress;
putWord16(result, idRangeOffsetAddress, idRangeOffset);
 
qDebug() << QChar(startCodes[i]) << "-" << QChar(endCodes[i]) << "/" << (endCodes[i]-startCodes[i]+1) << "@" << idRangeOffset << "+" << idDeltas[i];
 
uint startCode = startCodes[i];
uint segLength = (endCodes[i]-startCode+1);
for (uint offset = 0; offset < segLength; ++offset)
{
quint16 glyph = cmap[startCode + offset];
if (glyph != 0)
{
glyph -= idDeltas[i];
}
appendWord16(result, glyph);
}
}
};
putWord16(result, startOfTable + ttf_cmap4_length, result.size() - startOfTable);
return result;
}
QList<uint> copyGlyphComponents(QByteArray& destGlyf, const QByteArray& srcGlyf, uint srcOffset,
QMap<uint,uint> newForOldGid, uint& nextFreeGid)
{
QList<uint> result;
uint destStart = destGlyf.size();
destGlyf.resize(destStart + ttf_glyf_headerSize);
copy(destGlyf, destStart, srcGlyf, srcOffset, ttf_glyf_headerSize);
uint pos = srcOffset + ttf_glyf_headerSize;
bool haveInstructions = false;
uint flags = 0;
do {
/* flags */
flags = word16(srcGlyf, pos);
pos += 2;
haveInstructions |= (flags & ttf_glyf_ComponentFlag_WE_HAVE_INSTRUCTIONS);
appendWord16(destGlyf, flags);
/* glyphindex */
uint glyphIndex = word16(srcGlyf, pos);
pos += 2;
if (newForOldGid.contains(glyphIndex))
{
glyphIndex = newForOldGid[glyphIndex];
}
else
{
glyphIndex = nextFreeGid++;
result.append(glyphIndex);
newForOldGid[glyphIndex] = glyphIndex;
}
appendWord16(destGlyf, glyphIndex);
/* args */
if ( flags & ttf_glyf_ComponentFlag_ARG_1_AND_2_ARE_WORDS) {
appendWord16(destGlyf, word16(srcGlyf, pos)); // arg1
pos += 2;
appendWord16(destGlyf, word16(srcGlyf, pos)); // arg2
pos += 2;
}
else {
appendWord16(destGlyf, word16(srcGlyf, pos)); // arg1and2
pos += 2;
}
if ( flags & ttf_glyf_ComponentFlag_WE_HAVE_A_SCALE ) {
appendWord16(destGlyf, word16(srcGlyf, pos)); // scale
pos += 2;
} else if ( flags & ttf_glyf_ComponentFlag_WE_HAVE_AN_X_AND_Y_SCALE ) {
appendWord16(destGlyf, word16(srcGlyf, pos)); // xscale
pos += 2;
appendWord16(destGlyf, word16(srcGlyf, pos)); // yscale
pos += 2;
} else if ( flags & ttf_glyf_ComponentFlag_WE_HAVE_A_TWO_BY_TWO ) {
appendWord16(destGlyf, word16(srcGlyf, pos)); // xscale
pos += 2;
appendWord16(destGlyf, word16(srcGlyf, pos)); // scale01
pos += 2;
appendWord16(destGlyf, word16(srcGlyf, pos)); // scale10
pos += 2;
appendWord16(destGlyf, word16(srcGlyf, pos)); // yscale
pos += 2;
}
} while ( flags & ttf_glyf_ComponentFlag_MORE_COMPONENTS );
 
if (haveInstructions)
{
uint numInstr = word16(srcGlyf, pos);
appendWord16(destGlyf, numInstr);
pos += 2;
uint destPos = destGlyf.size();
destGlyf.resize(destPos + numInstr);
copy(destGlyf, destPos, srcGlyf, pos, numInstr);
}
return result;
}
QList<uint> copyGlyph(QList<uint>& destLoca, QByteArray& destGlyf, uint destGid,
const QList<uint>& srcLoca, const QByteArray& srcGlyf, uint srcGid,
QMap<uint,uint>& newForOldGid, uint& nextFreeGid)
{
QList<uint> compositeElements;
uint glyphStart = srcLoca[srcGid];
uint glyphLength = srcLoca[srcGid+1] - glyphStart;
destLoca.append(destGlyf.size());
int i = 0;
if (glyphLength > 0)
{
uint nrOfContours = word16(srcGlyf, glyphStart);
if (nrOfContours > 0)
{
// simple glyph
uint destStart = destGlyf.size();
qDebug() << i++ << ":" << nrOfContours << "contours" << glyphStart << "-->" << destStart << "/" << glyphLength;
destGlyf.resize(destStart + glyphLength);
copy(destGlyf, destStart, srcGlyf, glyphStart, glyphLength);
}
else
{
compositeElements.append(copyGlyphComponents(destGlyf, srcGlyf, glyphStart, newForOldGid, nextFreeGid));
qDebug() << i++ << ":" << srcGid << "composite glyph brought" << compositeElements.size() << "more glyphs";
}
}
 
return compositeElements;
}
QByteArray subsetFace(const QByteArray& ttf, QList<uint>& glyphs)
{
QMap<QByteArray,QByteArray> tables;
// qDebug() << "loca table:" << (void*) oldLoca[0] << (void*) oldLoca[1] << (void*) oldLoca[2] << (void*) oldLoca[3] << (void*) oldLoca[4] << (void*) oldLoca[5] << (void*) oldLoca[6] << (void*) oldLoca[7];
 
QMap<uint,uint> newForOldGid;
if (glyphs.length() == 0)
{
tables["loca"] = getTable(ttf, "loca");
tables["glyf"] = getTable(ttf, "glyf");
}
else
{
QList<uint> oldLoca = readLoca(ttf);
const QByteArray oldGlyf = getTable(ttf, "glyf");
 
QList<quint32> newLoca;
QByteArray newGlyf;
glyphs.removeAll(0);
glyphs.prepend(0);
uint nextFreeGid = glyphs.length();
for (int i = 0; i < glyphs.length(); ++i)
{
uint oldGid = glyphs[i];
newForOldGid[oldGid] = i;
glyphs.append(copyGlyph(newLoca, newGlyf, i,
oldLoca, oldGlyf, oldGid,
newForOldGid, nextFreeGid));
}
newLoca.append(newGlyf.length());
tables["loca"] = writeLoca(newLoca, hasLongLocaFormat(ttf));
tables["glyf"] = newGlyf;
}
QMap<uint,uint> cmap = readCMap(ttf);
QMap<uint,uint>::iterator it;
uint firstChar = 0xFFFFFFFF;
uint lastChar = 0;
for (it = cmap.begin(); it != cmap.end(); ++it)
{
if (glyphs.length() > 0 && !glyphs.contains(it.value()))
{
it.value() = 0;
}
else if (it.value() != 0)
{
if (glyphs.length() > 0)
{
qDebug() << "MAP" << QChar(it.key()) << it.value() << "-->" << newForOldGid[it.value()];
it.value() = newForOldGid[it.value()];
}
if (it.key() < firstChar)
firstChar = it.key();
else if (it.key() > lastChar)
lastChar = it.key();
}
}
tables["cmap"] = writeCMap(cmap);
 
QByteArray os2 = getTable(ttf, "OS/2");
if (os2.length() > ttf_os2_usLastCharIndex)
{
// TODO: adapt unicode ranges
putWord16(os2, ttf_os2_usFirstCharIndex, firstChar < 0xFFFF ? firstChar : 0xFFFF);
putWord16(os2, ttf_os2_usLastCharIndex, lastChar < 0xFFFF ? lastChar : 0xFFFF);
tables["OS/2"] = os2;
}
if (glyphs.length() > 0)
{
QList<std::pair<qint16, quint16> > oldHmtx = readHmtx(ttf);
QList<std::pair<qint16, quint16> > newHmtx;
newHmtx.append(std::pair<qint16, quint16>(1234, 123));
for (int i = 1; i < glyphs.length(); ++i)
newHmtx.append(newHmtx[0]);
QMap<uint,uint>::const_iterator iter;
for (iter = newForOldGid.cbegin(); iter != newForOldGid.cend(); ++iter)
{
qDebug() << "hmtx" << iter.key() << " -> " << iter.value() << "=" << oldHmtx[iter.key()].first;
newHmtx[iter.value()] = oldHmtx[iter.key()];
}
tables["hmtx"] = writeHmtx(newHmtx);
}
else
{
tables["hmtx"] = getTable(ttf, "hmtx");
}
QByteArray maxp = getTable(ttf, "maxp");
if (glyphs.length() > 0)
{
putWord16(maxp, ttf_maxp_numGlyphs, glyphs.length());
}
tables["maxp"] = maxp;
QByteArray hhea = getTable(ttf, "hhea");
if (glyphs.length() > 0)
{
putWord16(hhea, ttf_hhea_numOfLongHorMetrics, glyphs.length());
}
tables["hhea"] = hhea;
QByteArray post = getTable(ttf, "post");
if (word(post, ttf_post_format) != post_format30)
{
putWord(post, ttf_post_format, post_format30);
post.truncate(ttf_post_header_length);
}
tables["post"] = post;
// TODO: kern table
QByteArray name = getTable(ttf, "name");
if (name.length() > 0)
tables["name"] = name;
QByteArray prep = getTable(ttf, "prep");
if (prep.length() > 0)
tables["prep"] = prep;
QByteArray cvt = getTable(ttf, "cvt ");
if (cvt.length() > 0)
tables["cvt "] = cvt;
 
QByteArray fpgm = getTable(ttf, "fpgm");
if (fpgm.length() > 0)
tables["fpgm"] = fpgm;
 
QByteArray head = getTable(ttf, "head");
putWord(head, ttf_head_checkSumAdjustment, 0);
tables["head"] = head;
 
QByteArray font = createTableDir(tables.keys());
QMap<QByteArray,QByteArray>::iterator tableP;
for (tableP = tables.begin(); tableP != tables.end(); ++tableP)
{
writeTable(font, tableP.key(), tableP.value());
}
uint checkSumAdjustment = 0xB1B0AFBA - calcTableChecksum(font);
uint headTable = getTableDirEntry(font, "head");
headTable = word(font, headTable + ttf_TableRecord_offset);
putWord(font, headTable + ttf_head_checkSumAdjustment, checkSumAdjustment);
// done!
return font;
}
} // namespace sfnt
 
 
/// For GPOS lookups
enum TTF_GPOS_ValueFormat
{
XPlacement = 0x0001,
YPlacement = 0x0002,
XAdvance = 0x0004,
YAdvance = 0x0008,
XPlaDevice =0x0010,
YPlaDevice =0x0020,
XAdvDevice =0x0040,
YAdvDevice =0x0080
};
 
 
 
KernFeature::KernFeature ( FT_Face face ) : m_valid ( true )
{
FontName = QString (face->family_name) + " " + QString (face->style_name);
// qDebug() <<"KF"<<FontName;
// QTime t;
// t.start();
FT_ULong length = 0;
if ( !FT_Load_Sfnt_Table ( face, TTAG_GPOS , 0, NULL, &length ) )
{
// qDebug() <<"\t"<<"GPOS table len"<<length;
if ( length > 32 )
{
GPOSTableRaw.resize ( length );
FT_Load_Sfnt_Table ( face, TTAG_GPOS, 0, reinterpret_cast<FT_Byte*> ( GPOSTableRaw.data() ), &length );
makeCoverage();
}
else
m_valid = false;
GPOSTableRaw.clear();
// coverages.clear();
}
else
m_valid = false;
if (!m_valid)
pairs.clear();
// qDebug() <<"\t"<<m_valid;
// qDebug() <<"\t"<<t.elapsed();
}
 
KernFeature::KernFeature ( const KernFeature & kf )
{
m_valid = kf.m_valid;
if ( m_valid )
pairs = kf.pairs;
}
 
 
KernFeature::~ KernFeature()
{
}
 
double KernFeature::getPairValue ( unsigned int glyph1, unsigned int glyph2 ) const
{
if (!m_valid)
return 0.0;
if (pairs.contains(glyph1) &&
pairs[glyph1].contains(glyph2))
{
return pairs[glyph1][glyph2];
}
//qDebug()<<"Search in classes";
foreach (const quint16& coverageId, coverages.keys())
{
// for each pairpos table, coverage lists covered _first_ (left) glyph
if (!coverages[coverageId].contains(glyph1))
continue;
foreach(const quint16& classDefOffset, classGlyphFirst[coverageId].keys())
{
const ClassDefTable& cdt(classGlyphFirst[coverageId][classDefOffset]);
foreach(const quint16& classIndex, cdt.keys())
{
const QList<quint16>& gl(cdt[classIndex]);
if (!gl.contains(glyph1))
continue;
//qDebug()<<"Found G1"<<glyph1<<"in Class"<<classIndex<<"at pos"<<gl.indexOf(glyph1);
// Now we got the index of the first glyph class, see if glyph2 is in one of the left glyphs classes attached to this subtable.
foreach(const quint16& classDefOffset2, classGlyphSecond[coverageId].keys())
{
const ClassDefTable& cdt2(classGlyphSecond[coverageId][classDefOffset2]);
foreach(const quint16& classIndex2, cdt2.keys())
{
const QList<quint16>& gl2(cdt2[classIndex2]);
if (gl2.contains(glyph2))
{
//qDebug()<<"Found G2"<<glyph2<<"in Class"<<classIndex2<<"at pos"<<gl2.indexOf(glyph2);
double v(classValue[coverageId][classIndex][classIndex2]);
// Cache this pair into "pairs" map.
pairs[glyph1][glyph2] = v;
return v;
}
}
}
}
}
}
return 0.0;
}
 
void KernFeature::makeCoverage()
{
if ( GPOSTableRaw.isEmpty() )
return;
quint16 FeatureList_Offset= toUint16 ( 6 );
quint16 LookupList_Offset = toUint16 ( 8 );
// Find the offsets of the kern feature tables
quint16 FeatureCount = toUint16 ( FeatureList_Offset );
QList<quint16> FeatureKern_Offset;
for ( quint16 FeatureRecord ( 0 ); FeatureRecord < FeatureCount; ++ FeatureRecord )
{
int rawIdx ( FeatureList_Offset + 2 + ( 6 * FeatureRecord ) );
quint32 tag ( FT_MAKE_TAG ( GPOSTableRaw.at ( rawIdx ),
GPOSTableRaw.at ( rawIdx + 1 ),
GPOSTableRaw.at ( rawIdx + 2 ),
GPOSTableRaw.at ( rawIdx + 3 ) ) );
if ( tag == TTAG_kern )
{
FeatureKern_Offset << ( toUint16 ( rawIdx + 4 ) + FeatureList_Offset );
}
}
// Extract indices of lookups for feature kern
QList<quint16> LookupListIndex;
foreach ( quint16 kern, FeatureKern_Offset )
{
quint16 LookupCount ( toUint16 ( kern + 2 ) );
for ( int llio ( 0 ) ; llio < LookupCount; ++llio )
{
quint16 Idx ( toUint16 ( kern + 4 + ( llio * 2 ) ) );
if ( !LookupListIndex.contains ( Idx ) )
{
LookupListIndex <<Idx ;
}
}
}
// Extract offsets of lookup tables for feature kern
QList<quint16> LookupTables;
QList<quint16> PairAdjustmentSubTables;
for ( int i ( 0 ); i < LookupListIndex.count(); ++i )
{
int rawIdx ( LookupList_Offset + 2 + ( LookupListIndex[i] * 2 ) );
quint16 Lookup ( toUint16 ( rawIdx ) + LookupList_Offset );
quint16 SubTableCount ( toUint16 ( Lookup + 4 ) );
for ( int stIdx ( 0 ); stIdx < SubTableCount; ++ stIdx )
{
quint16 SubTable ( toUint16 ( Lookup + 6 + ( 2 * stIdx ) ) + Lookup );
// quint16 PosFormat ( toUint16 ( SubTable ) );
quint16 Coverage_Offset ( toUint16 ( SubTable + 2 ) + SubTable );
quint16 CoverageFormat ( toUint16 ( Coverage_Offset ) );
if ( 1 == CoverageFormat ) // glyph indices based
{
quint16 GlyphCount ( toUint16 ( Coverage_Offset + 2 ) );
quint16 GlyphID ( Coverage_Offset + 4 );
if (GlyphCount == 0) continue;
for ( unsigned int gl ( 0 ); gl < GlyphCount; ++gl )
{
coverages[SubTable] << toUint16 ( GlyphID + ( gl * 2 ) );
}
}
else if ( 2 == CoverageFormat ) // Coverage Format2 => ranges based
{
quint16 RangeCount ( toUint16 ( Coverage_Offset + 2 ) );
if (RangeCount == 0) continue;
// int gl_base ( 0 );
for ( int r ( 0 ); r < RangeCount; ++r )
{
quint16 rBase ( Coverage_Offset + 4 + ( r * 6 ) );
quint16 Start ( toUint16 ( rBase ) );
quint16 End ( toUint16 ( rBase + 2 ) );
// quint16 StartCoverageIndex ( toUint16 ( rBase + 4 ) );
// #9842 : for some font such as Gabriola Regular
// the range maybe be specified in reverse order
if (Start <= End)
{
for ( unsigned int gl ( Start ); gl <= End; ++gl )
coverages[SubTable] << gl;
}
else
{
for ( int gl ( Start ); gl >= (int) End; --gl )
coverages[SubTable] << gl;
}
}
}
else
{
// qDebug() <<"Unknow Coverage Format:"<<CoverageFormat;
continue;
}
makePairs ( SubTable );
}
}
}
 
 
void KernFeature::makePairs ( quint16 subtableOffset )
{
/*
Lookup Type 2:
Pair Adjustment Positioning Subtable
*/
quint16 PosFormat ( toUint16 ( subtableOffset ) );
if ( PosFormat == 1 )
{
quint16 ValueFormat1 ( toUint16 ( subtableOffset +4 ) );
quint16 ValueFormat2 ( toUint16 ( subtableOffset +6 ) );
quint16 PairSetCount ( toUint16 ( subtableOffset +8 ) );
if ( ValueFormat1 && ValueFormat2 )
{
for ( int psIdx ( 0 ); psIdx < PairSetCount; ++ psIdx )
{
int oldSecondGlyph = -1;
unsigned int FirstGlyph ( coverages[subtableOffset][psIdx] );
quint16 PairSetOffset ( toUint16 ( subtableOffset +10 + ( 2 * psIdx ) ) + subtableOffset );
quint16 PairValueCount ( toUint16 ( PairSetOffset ) );
quint16 PairValueRecord ( PairSetOffset + 2 );
for ( int pvIdx ( 0 ); pvIdx < PairValueCount; ++pvIdx )
{
quint16 recordBase ( PairValueRecord + ( ( 2 + 2 + 2 ) * pvIdx ) );
quint16 SecondGlyph ( toUint16 ( recordBase ) );
qint16 Value1 ( toInt16 ( recordBase + 2 ) );
// #12475 : Per OpenType spec PairValueRecords must be sorted by SecondGlyph.
// If a kerning pair is duplicated, take only the first one into account
// for now. In the future we may have to ignore the GPOS table in such case.
// (http://partners.adobe.com/public/developer/opentype/index_table_formats2.html)
if (oldSecondGlyph >= SecondGlyph)
continue;
pairs[FirstGlyph][SecondGlyph] = double ( Value1 );
oldSecondGlyph = SecondGlyph;
}
}
}
else if ( ValueFormat1 && ( !ValueFormat2 ) )
{
for ( int psIdx ( 0 ); psIdx < PairSetCount; ++ psIdx )
{
int oldSecondGlyph = -1;
unsigned int FirstGlyph ( coverages[subtableOffset][psIdx] );
quint16 PairSetOffset ( toUint16 ( subtableOffset +10 + ( 2 * psIdx ) ) + subtableOffset );
quint16 PairValueCount ( toUint16 ( PairSetOffset ) );
quint16 PairValueRecord ( PairSetOffset + 2 );
for ( int pvIdx ( 0 ); pvIdx < PairValueCount; ++pvIdx )
{
quint16 recordBase ( PairValueRecord + ( ( 2 + 2 ) * pvIdx ) );
quint16 SecondGlyph ( toUint16 ( recordBase ) );
qint16 Value1 ( toInt16 ( recordBase + 2 ) );
// #12475 : Per OpenType spec PairValueRecords must be sorted by SecondGlyph.
// If a kerning pair is duplicated, take only the first one into account
// for now. In the future we may have to ignore the GPOS table in such case.
// (http://partners.adobe.com/public/developer/opentype/index_table_formats2.html)
if (oldSecondGlyph >= SecondGlyph)
continue;
pairs[FirstGlyph][SecondGlyph] = double ( Value1 );
oldSecondGlyph = SecondGlyph;
}
}
}
else
{
// qDebug() <<"ValueFormat1 is null or both ValueFormat1 and ValueFormat2 are null";
}
}
else if ( PosFormat == 2 ) // class kerning
{
quint16 ValueFormat1 ( toUint16 ( subtableOffset +4 ) );
quint16 ValueFormat2 ( toUint16 ( subtableOffset +6 ) );
quint16 ClassDef1 ( toUint16 ( subtableOffset +8 ) + subtableOffset );
quint16 ClassDef2 ( toUint16 ( subtableOffset +10 ) + subtableOffset );
quint16 Class1Count ( toUint16 ( subtableOffset +12 ) );
quint16 Class2Count ( toUint16 ( subtableOffset +14 ) );
quint16 Class1Record ( subtableOffset +16 );
// first extract classses
getClass(true, ClassDef1 , subtableOffset );
getClass(false, ClassDef2 , subtableOffset );
if ( ValueFormat1 && ValueFormat2 )
{
for ( quint16 C1 ( 0 );C1 < Class1Count; ++C1 )
{
quint16 Class2Record ( Class1Record + ( C1 * ( 2 * 2 * Class2Count ) ) );
for ( quint16 C2 ( 0 );C2 < Class2Count; ++C2 )
{
qint16 Value1 ( toInt16 ( Class2Record + ( C2 * ( 2 * 2 ) ) ) );
if (Value1 != 0)
{
classValue[subtableOffset][C1][C2] = double ( Value1 );
}
}
}
}
else if ( ValueFormat1 && ( !ValueFormat2 ) )
{
for ( quint16 C1 ( 1 );C1 < Class1Count; ++C1 )
{
quint16 Class2Record ( Class1Record + ( C1 * ( 2 * Class2Count ) ) );
for ( quint16 C2 ( 1 );C2 < Class2Count; ++C2 )
{
qint16 Value1 ( toInt16 ( Class2Record + ( C2 * 2 ) ) );
if (Value1 != 0)
{
classValue[subtableOffset][C1][C2] = double ( Value1 );
}
}
}
}
else
{
// qDebug() <<"ValueFormat1 is null or both ValueFormat1 and ValueFormat2 are null";
}
}
else
qDebug() <<"unknown PosFormat"<<PosFormat;
}
 
KernFeature::ClassDefTable KernFeature::getClass ( bool leftGlyph, quint16 classDefOffset, quint16 coverageId )
{
if (leftGlyph)
{
if (classGlyphFirst.contains(coverageId) && classGlyphFirst[coverageId].contains(classDefOffset))
return classGlyphFirst[coverageId][classDefOffset];
}
else
{
if (classGlyphSecond.contains(coverageId) && classGlyphSecond[coverageId].contains(classDefOffset))
return classGlyphSecond[coverageId][classDefOffset];
}
ClassDefTable ret;
QList<quint16> excludeList;
quint16 ClassFormat ( toUint16 ( classDefOffset ) );
if ( ClassFormat == 1 )
{
quint16 StartGlyph ( toUint16 ( classDefOffset +2 ) );
quint16 GlyphCount ( toUint16 ( classDefOffset +4 ) );
quint16 ClassValueArray ( classDefOffset + 6 );
for ( quint16 CV ( 0 );CV < GlyphCount; ++CV )
{
excludeList<<StartGlyph + CV;
ret[ toUint16 ( ClassValueArray + ( CV * 2 ) ) ] << StartGlyph + CV;
}
}
else if ( ClassFormat == 2 )
{
quint16 ClassRangeCount ( toUint16 ( classDefOffset + 2 ) );
quint16 ClassRangeRecord ( classDefOffset + 4 );
for ( int CRR ( 0 ); CRR < ClassRangeCount; ++CRR )
{
quint16 Start ( toUint16 ( ClassRangeRecord + ( CRR * 6 ) ) );
quint16 End ( toUint16 ( ClassRangeRecord + ( CRR * 6 ) + 2 ) );
quint16 Class ( toUint16 ( ClassRangeRecord + ( CRR * 6 ) + 4 ) );
if (Start <= End)
{
for ( int gl ( Start ); gl <= (int) End; ++gl )
{
excludeList<< (quint16) gl;
ret[Class] << gl;
}
}
else
{
for ( int gl ( Start ); gl >= (int) End; --gl )
{
excludeList<< (quint16) gl;
ret[Class] << gl;
}
}
}
}
else
qDebug() <<"Unknown Class Table type";
// if possible (all glyphs are "classed"), avoid to pass through this slow piece of code.
if (excludeList.count() != coverages[coverageId].count())
{
foreach(const quint16& gidx, coverages[coverageId])
{
if (!excludeList.contains(gidx))
ret[0] << gidx;
}
}
if (leftGlyph)
classGlyphFirst[coverageId][classDefOffset] = ret;
else
classGlyphSecond[coverageId][classDefOffset] = ret;
return ret;
}
 
quint16 KernFeature::toUint16 ( quint16 index )
{
if ( ( index + 2 ) > GPOSTableRaw.count() )
{
// qDebug() << "HORROR!" << index << GPOSTableRaw.count() << FontName ;
// Rather no kerning at all than random kerning
// m_valid = false;
return 0;
}
// FIXME I just do not know how it has to be done *properly*
quint8 c1 ( GPOSTableRaw.at ( index ) );
quint8 c2 ( GPOSTableRaw.at ( index + 1 ) );
quint16 ret ( ( c1 << 8 ) | c2 );
return ret;
}
 
qint16 KernFeature::toInt16 ( quint16 index )
{
if ( ( index + 2 ) > GPOSTableRaw.count() )
{
return 0;
}
// FIXME I just do not know how it has to be done *properly*
quint8 c1 ( GPOSTableRaw.at ( index ) );
quint8 c2 ( GPOSTableRaw.at ( index + 1 ) );
qint16 ret ( ( c1 << 8 ) | c2 );
return ret;
}
/trunk/Scribus/scribus/fonts/sfnt.h
23,14 → 23,20
 
namespace sfnt {
uchar byte(QByteArray const & bb, uint pos);
uint word(QByteArray const & bb, uint pos);
void putWord(QByteArray & bb, uint pos, uint val);
uint word16(QByteArray const & bb, uint pos);
bool copy(QByteArray & dst, uint to, QByteArray & src, uint from, uint len);
QByteArray tag(QByteArray const & bb, uint pos);
uchar byte(QByteArray const & bb, uint pos);
quint32 word(QByteArray const & bb, uint pos);
void putWord(QByteArray & bb, uint pos, quint32 val);
void appendWord(QByteArray& bb, quint32 val);
quint16 word16(QByteArray const & bb, uint pos);
void putWord16(QByteArray & bb, uint pos, quint16 val);
void appendWord16(QByteArray& bb, quint16 val);
bool copy(QByteArray & dst, uint to, const QByteArray & src, uint from, uint len);
const QByteArray tag(QByteArray const & bb, uint pos);
const QByteArray getTable(const QByteArray& ttf, const QByteArray& ttfTag);
 
QByteArray subsetFace(const QByteArray& ttf, QList<uint>& glyphs);
QByteArray extractFace(const QByteArray& ttfColl, int faceIndex);
 
/**
This class checks the post table of a ttf font.
39,7 → 45,7
public:
bool usable;
QString errorMsg;
int numberOfGlyphs() const;
uint numberOfGlyphs() const;
QString nameFor(uint glyphId) const;
void readFrom(FT_Face face);
private:
49,4 → 55,56
} //namespace
 
 
/**
An object holding a table of kerning pairs extracted from
a kern feature such as found in a GPOS table
*/
class SCRIBUS_API KernFeature
{
typedef QMap<quint16, QList<quint16> > ClassDefTable; // <Class index (0 to N) , list of glyphs >
public:
/**
* Build a ready-to-use kerning pairs table
* @param face a valid FT_Face, It won’t be store by KernFeature
*/
KernFeature ( FT_Face face );
KernFeature ( const KernFeature& kf );
~KernFeature();
/**
* Get the kerning value for a pair of glyph indexes.
* @param glyph1 Index of the left glyph in logical order
* @param glyph2 Index of the right glyph in logical order
* @return the unscaled delta to apply to xadvance of the first glyph
*/
double getPairValue ( unsigned int glyph1, unsigned int glyph2 ) const;
/**
* The table can have been invalidated if something went wrong at any moment.
* @return True if valid, False otherwise.
*/
bool isValid() const {return m_valid;}
private:
bool m_valid;
QByteArray GPOSTableRaw;
QMap<quint16,QList<quint16> > coverages;
mutable QMap<quint16, QMap<quint16, double> > pairs;
QMap< quint16, QMap<quint16, ClassDefTable> > classGlyphFirst; // < subtable offset, map<offset, class definition table> > for first glyph
QMap< quint16, QMap<quint16, ClassDefTable> > classGlyphSecond; // < subtable offset, map<offset, class definition table> > for second glyph
QMap< quint16, QMap<int, QMap<int, double> > > classValue; // < subtable offset, map<class1, map<class2, value> > >
void makeCoverage();
void makePairs ( quint16 subtableOffset );
ClassDefTable getClass (bool leftGlyph, quint16 classDefOffset, quint16 coverageId );
inline quint16 toUint16 ( quint16 index );
inline qint16 toInt16 ( quint16 index );
QString FontName;// for debugging purpose
};
 
 
#endif
 
/trunk/Scribus/scribus/fonts/sfnt_format.h
0,0 → 1,209
//
// sfnt_format.h
// Scribus
//
// Created by Andreas Vox on 26.04.15.
//
//
 
#ifndef Scribus_sfnt_format_h
#define Scribus_sfnt_format_h
 
namespace sfnt {
enum TTC_Format {
ttc_Tag = 0,
ttc_Version = 4,
ttc_numFonts = 8,
ttc_OffsetTables = 12,
ttc_ulDsigTag_OffsetTables = 0,
ttc_ulDsigLength_OffsetTables = 4,
ttc_ulDsigOffset_OffsetTables = 8
};
 
 
enum TTF_Format {
ttf_sfnt_version = 0,
ttf_numtables = 4,
ttf_searchRange = 6,
ttf_entrySelector = 8,
ttf_rangeShift = 10,
ttf_TableRecords = 12,
ttf_TableRecord_tag = 0,
ttf_TableRecord_checkSum = 4,
ttf_TableRecord_offset = 8,
ttf_TableRecord_length = 12,
ttf_TableRecord_Size = 16
};
 
 
enum TTF_head_Format {
ttf_head_version = 0,
ttf_head_fontRevision = 4,
ttf_head_checkSumAdjustment = 8,
ttf_head_magicNumber = 12,
ttf_head_flags = 16,
ttf_head_unitsPerEm = 18,
ttf_head_created = 20,
ttf_head_modified = 28,
ttf_head_xMin = 36,
ttf_head_yMin = 38,
ttf_head_xMax = 40,
ttf_head_yMax = 42,
ttf_head_macStyle = 44,
ttf_head_lowestRecPPEM = 46,
ttf_head_fontDirectionHint = 48,
ttf_head_indexToLocFormat = 50,
ttf_head_glyphDataFormat = 52
};
 
 
enum TTF_cmap_Format {
ttf_cmap_version = 0,
ttf_cmap_numberSubtables = 2,
ttf_cmap_encodings = 4,
ttf_cmap_encoding_platformID = 0,
ttf_cmap_encoding_platformSpecificID = 2,
ttf_cmap_encoding_offset = 4,
ttf_cmap_encoding_Size = 8,
ttf_cmapx_format = 0,
ttf_cmap4_length = 2,
ttf_cmap4_language = 4,
ttf_cmap4_segCountX2 = 6,
ttf_cmap4_searchRange = 8,
ttf_cmap4_entrySelector = 10,
ttf_cmap4_rangeShift = 12,
ttf_cmap4_EndCodes = 14,
ttf_cmap4_reservePad_EndCodes = 0,
ttf_cmap4_StartCodes_EndCodes = 2,
ttf_cmap4_IdDeltas_StartCodes = 0,
ttf_cmap4_IdRangeOffsets_IdDeltas = 0,
ttf_cmap4_GlyphIds_IdRangeOffsets = 0,
ttf_cmap6_firstCode = 6,
ttf_cmap6_entryCount = 8,
ttf_cmap6_glyphIndexArray = 10,
ttf_cmap12_nGroups = 12,
ttf_cmap12_Groups = 16,
ttf_cmap12_Group_startCharCode = 0,
ttf_cmap12_Group_endCharCode = 4,
ttf_cmap12_Group_startGlyphCode = 8,
ttf_cmap12_Group_Size = 12
};
 
 
enum TTF_glyf_Format {
ttf_glyf_numberOfContours = 0,
ttf_glyf_xMin = 2,
ttf_glyf_yMin = 4,
ttf_glyf_xMax = 6,
ttf_glyf_yMax = 8,
ttf_glyf_headerSize = 10,
ttf_glyf_Components = 10,
ttf_glyf_Component_flags = 0,
ttf_glyf_Component_glyphIndex = 2,
ttf_glyf_Component_arguments = 4
};
 
 
enum TTF_glyf_ComponentFlags {
ttf_glyf_ComponentFlag_ARG_1_AND_2_ARE_WORDS = 0x0001,
ttf_glyf_ComponentFlag_ARGS_ARE_XYVALUES = 0x0002,
ttf_glyf_ComponentFlag_ROUND_XY_TO_GRID = 0x0004,
ttf_glyf_ComponentFlag_WE_HAVE_A_SCALE = 0x0008,
/* 0x0010 reserved */
ttf_glyf_ComponentFlag_MORE_COMPONENTS = 0x0020,
ttf_glyf_ComponentFlag_WE_HAVE_AN_X_AND_Y_SCALE = 0x0040,
ttf_glyf_ComponentFlag_WE_HAVE_A_TWO_BY_TWO = 0x0080,
ttf_glyf_ComponentFlag_WE_HAVE_INSTRUCTIONS = 0x0100,
ttf_glyf_ComponentFlag_USE_MY_METRICS = 0x0200,
ttf_glyf_ComponentFlag_OVERLAP_COMPOUND = 0x0400,
ttf_glyf_ComponentFlag_SCALED_COMPONENT_OFFSET = 0x0800,
ttf_glyf_ComponentFlag_UNSCALED_COMPONENT_OFFSET = 0x1000,
};
 
enum ttf_hhea_Format {
ttf_hhea_numOfLongHorMetrics = 34
};
 
enum ttf_maxp_Format {
ttf_maxp_version = 0,
ttf_maxp_numGlyphs = 4,
ttf_maxp_maxPoints = 6,
ttf_maxp_maxContours = 8,
ttf_maxp_maxComponentPoints = 10,
ttf_maxp_maxComponentContours = 12,
ttf_maxp_maxZones = 14,
ttf_maxp_maxTwilightPoints = 16,
ttf_maxp_maxStorage = 18,
ttf_maxp_maxFunctionDefs = 20,
ttf_maxp_maxInstructionDefs = 22,
ttf_maxp_maxStackElements = 24,
ttf_maxp_maxSizeOfInstructions = 26,
ttf_maxp_maxComponentElements = 28,
ttf_maxp_maxComponentDepth = 30,
ttf_maxp_Size = 32
};
 
enum TTF_os2_Format {
ttf_os2_version = 0,
ttf_os2_xAvgCharWidth = 2,
ttf_os2_usWeightClass = 4,
ttf_os2_usWidthClass = 6,
ttf_os2_fsType = 8,
ttf_os2_ySubscriptXSize = 10,
ttf_os2_ySubscriptYSize = 12,
ttf_os2_ySubscriptXOffset = 14,
ttf_os2_ySubscriptYOffset = 16,
ttf_os2_ySuperscriptXSize = 18,
ttf_os2_ySuperscriptYSize = 20,
ttf_os2_ySuperscriptXOffset = 22,
ttf_os2_ySuperscriptYOffset = 24,
ttf_os2_yStrikeoutSize = 26,
ttf_os2_yStrikeoutPosition = 28,
ttf_os2_sFamilyClass = 30,
ttf_os2_panose = 32,
ttf_os2_ulCharRange = 42,
ttf_os2_ulUnicodeRange1 = 42,
ttf_os2_ulUnicodeRange2 = 46,
ttf_os2_ulUnicodeRange3 = 50,
ttf_os2_ulUnicodeRange4 = 54,
ttf_os2_achVendID = 58,
ttf_os2_fsSelection = 62,
ttf_os2_usFirstCharIndex = 64,
ttf_os2_usLastCharIndex = 66,
ttf_os2_sTypeAscender = 68,
ttf_os2_sTypeoDescender = 70,
ttf_os2_sTypoLineGap = 72,
ttf_os2_usWinAscent = 74,
ttf_os2_usWinDescent = 76,
ttf_os2_f1_ulCodePageRange1 = 78,
ttf_os2_f1_ulCodePageRange2 = 82,
ttf_os2_f2_sxHeight = 86,
ttf_os2_f2_CapHeight = 88,
ttf_os2_f2_usDefaultChar = 90,
ttf_os2_f2_usBreakChar = 92,
ttf_os2_f2_usMaxContext = 94,
ttf_os2_f5_usLowerOpticalPointSize = 96,
ttf_os2_f5_usUpperOpticalPointSize = 98
};
enum TTF_post_Format {
ttf_post_format = 0,
/* more fields */
ttf_post_header_length = 32
};
 
 
enum TTF_post_format {
post_format10 = 0x00010000,
post_format20 = 0x00020000,
post_format25 = 0x00025000,
post_format30 = 0x00030000,
post_format40 = 0x00040000,
};
} // namespace
 
#endif
/trunk/Scribus/scribus/pdflib_core.cpp
71,6 → 71,8
#include "sccolor.h"
#include "sccolorengine.h"
#include "scfonts.h"
#include "fonts/cff.h"
#include "fonts/sfnt.h"
#include "scpage.h"
#include "scpaths.h"
#include "scpattern.h"
109,18 → 111,18
Options(doc.pdfOptions()),
Bvie(0),
ucs2Codec(0),
ObjCounter(7),
// ObjCounter(7),
ResNam("RE"),
ResCount(0),
NDnam("LI"),
NDnum(0),
KeyGen(""),
OwnerKey(""),
UserKey(""),
FileID(""),
EncryKey(""),
Encrypt(0),
KeyLen(5),
// KeyGen(""),
// OwnerKey(""),
// UserKey(""),
// FileID(""),
// EncryKey(""),
// Encrypt(0),
// KeyLen(5),
colorsToUse(),
spotNam("Spot"),
spotCount(0),
130,11 → 132,11
bleedDisplacementX(0),
bleedDisplacementY(0)
{
KeyGen.resize(32);
OwnerKey.resize(32);
UserKey.resize(32);
FileID.resize(16);
EncryKey.resize(5);
// KeyGen.resize(32);
// OwnerKey.resize(32);
// UserKey.resize(32);
// FileID.resize(16);
// EncryKey.resize(5);
Catalog.Outlines = 2;
Catalog.PageTree = 3;
Catalog.Dest = 4;
142,13 → 144,13
Outlines.First = 0;
Outlines.Last = 0;
Outlines.Count = 0;
Seite.ObjNum = 0;
Seite.Thumb = 0;
int kg_array[] = {0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41, 0x64, 0x00, 0x4e, 0x56, 0xff, 0xfa,
0x01, 0x08, 0x2e, 0x2e, 0x00, 0xb6, 0xd0, 0x68, 0x3e, 0x80, 0x2f, 0x0c, 0xa9, 0xfe,
0x64, 0x53, 0x69, 0x7a};
for (int a = 0; a < 32; ++a)
KeyGen[a] = kg_array[a];
pageData.ObjNum = 0;
pageData.Thumb = 0;
// int kg_array[] = {0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41, 0x64, 0x00, 0x4e, 0x56, 0xff, 0xfa,
// 0x01, 0x08, 0x2e, 0x2e, 0x00, 0xb6, 0xd0, 0x68, 0x3e, 0x80, 0x2f, 0x0c, 0xa9, 0xfe,
// 0x64, 0x53, 0x69, 0x7a};
// for (int a = 0; a < 32; ++a)
// KeyGen[a] = kg_array[a];
if (usingGUI)
{
progressDialog = new MultiProgressDialog( tr("Saving PDF"), CommonStrings::tr_Cancel, doc.scMW());
168,12 → 170,12
delete progressDialog;
}
 
static inline QString FToStr(double c)
static inline QByteArray FToStr(double c)
{
double v = c;
if (fabs(c) < 0.0000001)
v = 0.0;
return QString::number(v, 'f', 5);
return QByteArray::number(v, 'f', 5);
};
 
bool PDFLibCore::PDF_IsPDFX()
302,35 → 304,40
return abortExport;
}
 
void PDFLibCore::StartObj(int nr)
{
for (int i=XRef.size(); i < nr; ++i)
XRef.append(0);
XRef[nr-1] = bytesWritten();
PutDoc(QString::number(nr)+ " 0 obj\n");
}
//#define StartObj(n) writer.startObj((n))
#define PutDoc(s) writer.write(s)
//#define newObject() writer.newObject()
 
// Encode a string for inclusion in a
// PDF (literal) .
static QString PDFEncode(const QString & in)
{
QString tmp("");
for (int d = 0; d < in.length(); ++d)
{
QChar cc(in.at(d));
if ((cc == '(') || (cc == ')') || (cc == '\\'))
tmp += '\\';
else if ((cc == '\r') || (cc == '\n'))
{
tmp += (cc == '\r') ? "\\r" : "\\n";
continue;
}
tmp += cc;
}
return tmp;
}
 
static QString blendMode(int code)
//void PDFLibCore::StartObj(PdfId nr)
//{
// for (int i=XRef.size(); i < nr; ++i)
// XRef.append(0);
// XRef[nr-1] = bytesWritten();
// PutDoc(Pdf::toPdf(nr)+ " 0 obj\n");
//}
 
//// Encode a string for inclusion in a
//// PDF (literal) .
//static QByteArray PDFEncode(const QString & in)
//{
// QByteArray tmp("");
// for (int d = 0; d < in.length(); ++d)
// {
// QChar cc(in.at(d));
// if ((cc == '(') || (cc == ')') || (cc == '\\'))
// tmp += '\\';
// else if ((cc == '\r') || (cc == '\n'))
// {
// tmp += (cc == '\r') ? "\\r" : "\\n";
// continue;
// }
// tmp += cc;
// }
// return tmp;
//}
 
static QByteArray blendMode(int code)
{
switch (code)
{
386,96 → 393,99
return "";
}
}
//
//QByteArray PDFLibCore::EncodeUTF16(const QString &in)
//{
// QString tmp = in;
// QByteArray cres = ucs2Codec->fromUnicode( tmp );
//#ifndef WORDS_BIGENDIAN
// // on little endian systems we need to swap bytes:
// uchar sw;
// for(int d = 0; d < cres.size()-1; d += 2)
// {
// sw = cres[d];
// cres[d] = cres[d+1];
// cres[d+1] = sw;
// }
//#endif
// return cres;
//}
 
QByteArray PDFLibCore::EncodeUTF16(const QString &in)
QByteArray PDFLibCore::EncStream(const QByteArray & in, PdfId ObjNum)
{
QString tmp = in;
QByteArray cres = ucs2Codec->fromUnicode( tmp );
#ifndef WORDS_BIGENDIAN
// on little endian systems we need to swap bytes:
uchar sw;
for(int d = 0; d < cres.size()-1; d += 2)
{
sw = cres[d];
cres[d] = cres[d+1];
cres[d+1] = sw;
}
#endif
return cres;
}
 
QString PDFLibCore::EncStream(const QString & in, int ObjNum)
{
if (in.length() < 1)
return QString("");
return QByteArray();
else if (!Options.Encrypt)
return in;
rc4_context_t rc4;
QString tmp(in);
QByteArray us(tmp.length(), ' ');
QByteArray ou(tmp.length(), ' ');
for (int a = 0; a < tmp.length(); ++a)
us[a] = QChar(tmp.at(a)).cell();
QByteArray step1 = ComputeRC4Key(ObjNum);
rc4_init(&rc4, reinterpret_cast<uchar*>(step1.data()), qMin(KeyLen+5, 16));
rc4_encrypt(&rc4, reinterpret_cast<uchar*>(us.data()), reinterpret_cast<uchar*>(ou.data()), tmp.length());
QString uk = "";
for (int cl = 0; cl < tmp.length(); ++cl)
uk += QChar(ou[cl]);
return uk;
else
return writer.encryptBytes(in, ObjNum);
// rc4_context_t rc4;
// QByteArray tmp(in);
// QByteArray us(tmp.length(), ' ');
// QByteArray ou(tmp.length(), ' ');
// for (int a = 0; a < tmp.length(); ++a)
// us[a] = (tmp.at(a));
// QByteArray step1 = writer.ComputeRC4Key(ObjNum);
// rc4_init(&rc4, reinterpret_cast<uchar*>(step1.data()), qMin(KeyLen+5, 16));
// rc4_encrypt(&rc4, reinterpret_cast<uchar*>(us.data()), reinterpret_cast<uchar*>(ou.data()), tmp.length());
// QString uk = "";
// for (int cl = 0; cl < tmp.length(); ++cl)
// uk += QChar(ou[cl]);
// return ou;
}
 
QString PDFLibCore::EncString(const QString & in, int ObjNum)
QByteArray PDFLibCore::EncString(const QByteArray & in, PdfId ObjNum)
{
QString tmp;
QByteArray tmp;
if (in.length() < 1)
return "<>";
if (!Options.Encrypt)
{
tmp = "(" + PDFEncode(in) + ")";
return tmp;
//tmp = "(" + PDFEncode(in) + ")";
return Pdf::toLiteralString(in);
}
rc4_context_t rc4;
QByteArray us(in.length(), ' ');
QByteArray ou(in.length(), ' ');
for (int a = 0; a < in.length(); ++a)
us[a] = static_cast<uchar>(QChar(in.at(a)).cell());
QByteArray step1 = ComputeRC4Key(ObjNum);
rc4_init(&rc4, reinterpret_cast<uchar*>(step1.data()), qMin(KeyLen+5, 16));
rc4_encrypt(&rc4, reinterpret_cast<uchar*>(us.data()), reinterpret_cast<uchar*>(ou.data()), in.length());
QString uk = "";
for (int cl = 0; cl < in.length(); ++cl)
uk += QChar(ou[cl]);
tmp = "<"+String2Hex(&uk, false)+">";
return tmp;
// rc4_context_t rc4;
// QByteArray us(in.length(), ' ');
// QByteArray ou(in.length(), ' ');
// for (int a = 0; a < in.length(); ++a)
// us[a] = static_cast<uchar>(QChar(in.at(a)).cell());
// QByteArray step1 = writer.ComputeRC4Key(ObjNum);
// rc4_init(&rc4, reinterpret_cast<uchar*>(step1.data()), qMin(KeyLen+5, 16));
// rc4_encrypt(&rc4, reinterpret_cast<uchar*>(us.data()), reinterpret_cast<uchar*>(ou.data()), in.length());
// QString uk = "";
// for (int cl = 0; cl < in.length(); ++cl)
// uk += QChar(ou[cl]);
return Pdf::toHexString(writer.encryptBytes(in, ObjNum));
}
 
QString PDFLibCore::EncStringUTF16(const QString & in, int ObjNum)
QByteArray PDFLibCore::EncStringUTF16(const QString & in, PdfId ObjNum)
{
if (in.length() < 1)
return "<>";
if (!Options.Encrypt)
{
QByteArray us = EncodeUTF16(in);
QString uk = "";
for (int cl = 0; cl < us.size(); ++cl)
uk += QChar(us[cl]);
return "<"+String2Hex(&uk, false)+">";
QByteArray us = Pdf::toUTF16(in);
// QString uk = "";
// for (int cl = 0; cl < us.size(); ++cl)
// uk += QChar(us[cl]);
return Pdf::toHexString(us);
}
rc4_context_t rc4;
QByteArray us = EncodeUTF16(in);
QByteArray ou(us.size(), ' ');
QByteArray step1 = ComputeRC4Key(ObjNum);
rc4_init(&rc4, reinterpret_cast<uchar*>(step1.data()), qMin(KeyLen+5, 16));
rc4_encrypt(&rc4, reinterpret_cast<uchar*>(us.data()), reinterpret_cast<uchar*>(ou.data()), ou.size());
QString uk = "";
for (int cl = 0; cl < ou.size(); ++cl)
uk += QChar(ou[cl]);
QString tmp = "<"+String2Hex(&uk, false)+">";
// rc4_context_t rc4;
QByteArray us = Pdf::toUTF16(in);
// QByteArray ou(us.size(), ' ');
// QByteArray step1 = writer.ComputeRC4Key(ObjNum);
// rc4_init(&rc4, reinterpret_cast<uchar*>(step1.data()), qMin(KeyLen+5, 16));
// rc4_encrypt(&rc4, reinterpret_cast<uchar*>(us.data()), reinterpret_cast<uchar*>(ou.data()), ou.size());
// QString uk = "";
// for (int cl = 0; cl < ou.size(); ++cl)
// uk += QChar(ou[cl]);
QByteArray tmp = Pdf::toHexString(writer.encryptBytes(us, ObjNum));
return tmp;
}
 
bool PDFLibCore::EncodeArrayToStream(const QByteArray& in, int ObjNum)
bool PDFLibCore::EncodeArrayToStream(const QByteArray& in, PdfId ObjNum)
{
if (in.size() < 1)
return true;
482,70 → 492,47
bool succeed = false;
if (Options.Encrypt)
{
QByteArray step1 = ComputeRC4Key(ObjNum);
ScRC4EncodeFilter rc4Encode(&outStream, step1.data(), qMin(KeyLen+5, 16));
if (rc4Encode.openFilter())
ScStreamFilter* rc4Encode = writer.openStreamFilter(true, ObjNum);
if (rc4Encode->openFilter())
{
succeed = rc4Encode.writeData(in.data(), in.size());
succeed &= rc4Encode.closeFilter();
succeed = rc4Encode->writeData(in.data(), in.size());
succeed &= rc4Encode->closeFilter();
}
delete rc4Encode;
}
else
outStream.writeRawData(in, in.size());
return (outStream.status() == QDataStream::Ok);
writer.write(in);
return (writer.getOutStream().status() == QDataStream::Ok);
}
 
int PDFLibCore::WriteImageToStream(ScImage& image, int ObjNum, ColorSpaceEnum format, bool precal)
int PDFLibCore::WriteImageToStream(ScImage& image, PdfId ObjNum, ColorSpaceEnum format, bool precal)
{
bool fromCmyk, succeed = false;
int bytesWritten = 0;
if (Options.Encrypt)
{
QByteArray step1 = ComputeRC4Key(ObjNum);
ScRC4EncodeFilter rc4Encode(&outStream, step1.data(), qMin(KeyLen+5, 16));
if (rc4Encode.openFilter())
{
switch (format)
{
case ColorSpaceMonochrome :
fromCmyk = !Options.UseRGB && !Options.isGrayscale && !(doc.HasCMS && Options.UseProfiles2);
succeed = image.writeMonochromeDataToFilter(&rc4Encode, fromCmyk); break;
case ColorSpaceGray :
succeed = image.writeGrayDataToFilter(&rc4Encode, precal); break;
case ColorSpaceCMYK :
succeed = image.writeCMYKDataToFilter(&rc4Encode); break;
default :
succeed = image.writeRGBDataToFilter(&rc4Encode); break;
}
succeed &= rc4Encode.closeFilter();
bytesWritten = rc4Encode.writtenToStream();
}
}
else
{
ScNullEncodeFilter nullEncode(&outStream);
if (nullEncode.openFilter())
{
switch (format)
{
case ColorSpaceMonochrome :
fromCmyk = !Options.UseRGB && !Options.isGrayscale && !(doc.HasCMS && Options.UseProfiles2);
succeed = image.writeMonochromeDataToFilter(&nullEncode, fromCmyk); break;
case ColorSpaceGray :
succeed = image.writeGrayDataToFilter(&nullEncode, precal); break;
case ColorSpaceCMYK :
succeed = image.writeCMYKDataToFilter(&nullEncode); break;
default :
succeed = image.writeRGBDataToFilter(&nullEncode); break;
}
succeed &= nullEncode.closeFilter();
bytesWritten = nullEncode.writtenToStream();
}
}
 
ScStreamFilter* rc4Encode = writer.openStreamFilter(Options.Encrypt, ObjNum);
if (rc4Encode->openFilter())
{
switch (format)
{
case ColorSpaceMonochrome :
fromCmyk = !Options.UseRGB && !Options.isGrayscale && !(doc.HasCMS && Options.UseProfiles2);
succeed = image.writeMonochromeDataToFilter(rc4Encode, fromCmyk); break;
case ColorSpaceGray :
succeed = image.writeGrayDataToFilter(rc4Encode, precal); break;
case ColorSpaceCMYK :
succeed = image.writeCMYKDataToFilter(rc4Encode); break;
default :
succeed = image.writeRGBDataToFilter(rc4Encode); break;
}
succeed &= rc4Encode->closeFilter();
bytesWritten = rc4Encode->writtenToStream();
delete rc4Encode;
}
return (succeed ? bytesWritten : 0);
}
 
int PDFLibCore::WriteJPEGImageToStream(ScImage& image, const QString& fn, int ObjNum, int quality, ColorSpaceEnum format,
int PDFLibCore::WriteJPEGImageToStream(ScImage& image, const QString& fn, PdfId ObjNum, int quality, ColorSpaceEnum format,
bool sameFile, bool precal)
{
bool succeed = true;
568,18 → 555,18
if (Options.Encrypt)
{
succeed = false;
QByteArray step1 = ComputeRC4Key(ObjNum);
ScRC4EncodeFilter rc4Encode(&outStream, step1.data(), qMin(KeyLen+5, 16));
if (rc4Encode.openFilter())
ScStreamFilter* rc4Encode = writer.openStreamFilter(true, ObjNum);
if (rc4Encode->openFilter())
{
succeed = copyFileToFilter(jpgFileName, rc4Encode);
succeed &= rc4Encode.closeFilter();
bytesWritten = rc4Encode.writtenToStream();
succeed = copyFileToFilter(jpgFileName, *rc4Encode);
succeed &= rc4Encode->closeFilter();
bytesWritten = rc4Encode->writtenToStream();
}
delete rc4Encode;
}
else
{
succeed &= copyFileToStream(jpgFileName, outStream);
succeed &= copyFileToStream(jpgFileName, writer.getOutStream());
QFileInfo jpgInfo(jpgFileName);
bytesWritten = jpgInfo.size();
}
588,241 → 575,112
return (succeed ? bytesWritten : 0);
}
 
int PDFLibCore::WriteFlateImageToStream(ScImage& image, int ObjNum, ColorSpaceEnum format, bool precal)
int PDFLibCore::WriteFlateImageToStream(ScImage& image, PdfId ObjNum, ColorSpaceEnum format, bool precal)
{
bool fromCmyk, succeed = false;
int bytesWritten = 0;
if (Options.Encrypt)
{
QByteArray step1 = ComputeRC4Key(ObjNum);
ScRC4EncodeFilter rc4Encode(&outStream, step1.data(), qMin(KeyLen+5, 16));
ScFlateEncodeFilter flateEncode(&rc4Encode);
if (flateEncode.openFilter())
{
switch (format)
{
case ColorSpaceMonochrome :
fromCmyk = !Options.UseRGB && !Options.isGrayscale && !(doc.HasCMS && Options.UseProfiles2);
succeed = image.writeMonochromeDataToFilter(&flateEncode, fromCmyk); break;
case ColorSpaceGray :
succeed = image.writeGrayDataToFilter(&flateEncode, precal); break;
case ColorSpaceCMYK :
succeed = image.writeCMYKDataToFilter(&flateEncode); break;
default :
succeed = image.writeRGBDataToFilter(&flateEncode); break;
}
succeed &= flateEncode.closeFilter();
bytesWritten = flateEncode.writtenToStream();
}
}
else
{
ScFlateEncodeFilter flateEncode(&outStream);
if (flateEncode.openFilter())
{
switch (format)
{
case ColorSpaceMonochrome :
fromCmyk = !Options.UseRGB && !Options.isGrayscale && !(doc.HasCMS && Options.UseProfiles2);
succeed = image.writeMonochromeDataToFilter(&flateEncode, fromCmyk); break;
case ColorSpaceGray :
succeed = image.writeGrayDataToFilter(&flateEncode, precal); break;
case ColorSpaceCMYK :
succeed = image.writeCMYKDataToFilter(&flateEncode); break;
default :
succeed = image.writeRGBDataToFilter(&flateEncode); break;
}
succeed &= flateEncode.closeFilter();
bytesWritten = flateEncode.writtenToStream();
}
}
return (succeed ? bytesWritten : 0);
bool fromCmyk, succeed = false;
int bytesWritten = 0;
ScStreamFilter* rc4Encode = writer.openStreamFilter(Options.Encrypt, ObjNum);
ScFlateEncodeFilter flateEncode(rc4Encode);
if (flateEncode.openFilter())
{
switch (format)
{
case ColorSpaceMonochrome :
fromCmyk = !Options.UseRGB && !Options.isGrayscale && !(doc.HasCMS && Options.UseProfiles2);
succeed = image.writeMonochromeDataToFilter(&flateEncode, fromCmyk); break;
case ColorSpaceGray :
succeed = image.writeGrayDataToFilter(&flateEncode, precal); break;
case ColorSpaceCMYK :
succeed = image.writeCMYKDataToFilter(&flateEncode); break;
default :
succeed = image.writeRGBDataToFilter(&flateEncode); break;
}
succeed &= flateEncode.closeFilter();
bytesWritten = flateEncode.writtenToStream();
}
delete rc4Encode;
return (succeed ? bytesWritten : 0);
}
 
QString PDFLibCore::FitKey(const QString & pass)
{
QString pw(pass);
if (pw.length() < 32)
{
uint l = pw.length();
for (uint a = 0; a < 32 - l; ++a)
pw += QChar(KeyGen[a]);
}
else
pw = pw.left(32);
return pw;
}
 
void PDFLibCore::CalcOwnerKey(const QString & Owner, const QString & User)
{
rc4_context_t rc4;
QString pw(FitKey(User));
QString pw2(FitKey(Owner.isEmpty() ? User : Owner));
QByteArray step1(16, ' ');
step1 = ComputeMD5(pw2);
if (KeyLen > 5)
{
for (int kl = 0; kl < 50; ++kl)
step1 = QCryptographicHash::hash(step1, QCryptographicHash::Md5);
}
QByteArray us(32, ' ');
QByteArray enk(16, ' ');
if (KeyLen > 5)
{
for (uint a2 = 0; a2 < 32; ++a2)
OwnerKey[a2] = QChar(pw.at(a2)).cell();
for (int rl = 0; rl < 20; rl++)
{
for (int j = 0; j < 16; j ++)
enk[j] = step1[j] ^ rl;
rc4_init(&rc4, reinterpret_cast<uchar*>(enk.data()), 16);
rc4_encrypt(&rc4, reinterpret_cast<uchar*>(OwnerKey.data()),
reinterpret_cast<uchar*>(OwnerKey.data()), 32);
}
}
else
{
for (uint a = 0; a < 32; ++a)
us[a] = static_cast<uchar>(QChar(pw.at(a)).cell());
rc4_init(&rc4, reinterpret_cast<uchar*>(step1.data()), 5);
rc4_encrypt(&rc4, reinterpret_cast<uchar*>(us.data()),
reinterpret_cast<uchar*>(OwnerKey.data()), 32);
}
}
 
void PDFLibCore::CalcUserKey(const QString & User, int Permission)
bool PDFLibCore::PDF_Begin_Doc(const QString& fn, SCFonts &AllFonts, const QMap<QString, QMap<uint, FPointArray> >& DocFonts, BookMView* vi)
{
rc4_context_t rc4;
QString pw(FitKey(User));
QByteArray step1(16, ' ');
QByteArray perm(4, ' ');
uint perm_value = static_cast<uint>(Permission);
perm[0] = perm_value;
perm[1] = perm_value >> 8;
perm[2] = perm_value >> 16;
perm[3] = perm_value >> 24;
for (uint a = 0; a < 32; ++a)
pw += QChar(OwnerKey[a]);
for (uint a1 = 0; a1 < 4; ++a1)
pw += QChar(perm[a1]);
for (uint a3 = 0; a3 < 16; ++a3)
pw += QChar(FileID[a3]);
step1 = ComputeMD5(pw);
if (KeyLen > 5)
{
for (int kl = 0; kl < 50; ++kl)
step1 = QCryptographicHash::hash(step1, QCryptographicHash::Md5);
EncryKey.resize(16);
}
for (int a2 = 0; a2 < KeyLen; ++a2)
EncryKey[a2] = step1[a2];
if (KeyLen > 5)
{
QString pr2("");
for (int kl3 = 0; kl3 < 32; ++kl3)
pr2 += QChar(KeyGen[kl3]);
for (uint a4 = 0; a4 < 16; ++a4)
pr2 += QChar(FileID[a4]);
step1 = ComputeMD5(pr2);
QByteArray enk(16, ' ');
for (uint a3 = 0; a3 < 16; ++a3)
UserKey[a3] = step1[a3];
for (int rl = 0; rl < 20; rl++)
{
for (int j = 0; j < 16; j ++)
enk[j] = EncryKey[j] ^ rl;
rc4_init(&rc4, reinterpret_cast<uchar*>(enk.data()), 16);
rc4_encrypt(&rc4, reinterpret_cast<uchar*>(UserKey.data()), reinterpret_cast<uchar*>(UserKey.data()), 16);
}
}
else
{
rc4_init(&rc4, reinterpret_cast<uchar*>(step1.data()), 5);
rc4_encrypt(&rc4, reinterpret_cast<uchar*>(KeyGen.data()), reinterpret_cast<uchar*>(UserKey.data()), 32);
}
}
if (!writer.open(fn))
return false;
QFileInfo fiBase(fn);
QString baseDir = fiBase.absolutePath();
 
QByteArray PDFLibCore::ComputeMD5(const QString& in)
{
uint inlen=in.length();
QByteArray TBytes(inlen, ' ');
for (uint a = 0; a < inlen; ++a)
TBytes[a] = static_cast<uchar>(QChar(in.at(a)).cell());
return QCryptographicHash::hash(TBytes, QCryptographicHash::Md5);
}
 
QByteArray PDFLibCore::ComputeRC4Key(int ObjNum)
{
int dlen = 0;
QByteArray data(10, ' ');
if (KeyLen > 5)
data.resize(21);
for (int cd = 0; cd < KeyLen; ++cd)
{
data[cd] = EncryKey[cd];
dlen++;
}
data[dlen++] = ObjNum;
data[dlen++] = ObjNum >> 8;
data[dlen++] = ObjNum >> 16;
data[dlen++] = 0;
data[dlen++] = 0;
QByteArray rc4Key(16, ' ');
rc4Key = QCryptographicHash::hash(data, QCryptographicHash::Md5);
rc4Key.resize(qMin(KeyLen+5, 16));
return rc4Key;
}
 
bool PDFLibCore::PDF_Begin_Doc(const QString& fn, SCFonts &AllFonts, QMap<QString, QMap<uint, FPointArray> > DocFonts, BookMView* vi)
{
Spool.setFileName(fn);
if (!Spool.open(QIODevice::WriteOnly))
return false;
outStream.setDevice(&Spool);
QString tmp;
QString ok = "";
QString uk = "";
QFileInfo fd;
QString fext;
int a;
// QString tmp;
// QFileInfo fd;
// QString fext;
inPattern = 0;
Bvie = vi;
BookMinUse = false;
UsedFontsP.clear();
UsedFontsF.clear();
writer.writeHeader(Options.Version);
// if (((Options.Version == PDFOptions::PDFVersion_15) || (Options.Version == PDFOptions::PDFVersion_X4)) && (Options.useLayers))
// ObjCounter = 10;
// else
// ObjCounter = 9;
// switch (Options.Version)
// {
// case PDFOptions::PDFVersion_X1a:
// case PDFOptions::PDFVersion_X3:
// case PDFOptions::PDFVersion_13:
// PutDoc("%PDF-1.3\n");
// break;
// case PDFOptions::PDFVersion_14:
// PutDoc("%PDF-1.4\n");
// break;
// case PDFOptions::PDFVersion_X4:
// case PDFOptions::PDFVersion_15:
// PutDoc("%PDF-1.5\n");
// break;
// }
// if (PDF_IsPDFX())
// ObjCounter++;
// PutDoc("%\xc7\xec\x8f\xa2\n");
 
PDF_Begin_Catalog();
PDF_Begin_MetadataAndEncrypt();
PDF_Begin_WriteUsedFonts(AllFonts, PDF_Begin_FindUsedFonts(AllFonts, DocFonts));
PDF_Begin_Colors();
PDF_Begin_Layers();
return true;
}
 
void PDFLibCore::PDF_Begin_Catalog()
{
writer.startObj(writer.CatalogObj);
PutDoc("<<\n/Type /Catalog");
PutDoc("\n/Outlines " + Pdf::toObjRef(writer.OutlinesObj) +
"\n/Pages " + Pdf::toObjRef(writer.PagesObj) +
"\n/Dests " + Pdf::toObjRef(writer.DestsObj) +
"\n/AcroForm " + Pdf::toObjRef(writer.AcroFormObj) +
"\n/Names "+ Pdf::toObjRef(writer.NamesObj) +
"\n/Threads " + Pdf::toObjRef(writer.ThreadsObj) +
"\n");
if (((Options.Version == PDFOptions::PDFVersion_15) || (Options.Version == PDFOptions::PDFVersion_X4)) && (Options.useLayers))
ObjCounter = 10;
else
ObjCounter = 9;
switch (Options.Version)
{
writer.OCPropertiesObj = writer.newObject();
PutDoc("/OCProperties " + Pdf::toObjRef(writer.OCPropertiesObj) + "\n");
}
if (PDF_IsPDFX())
{
writer.OutputIntentObj = writer.newObject();
PutDoc("/OutputIntents [ " + Pdf::toObjRef(writer.OutputIntentObj) + " ]\n");
}
if ((Options.Version == PDFOptions::PDFVersion_X4))
{
case PDFOptions::PDFVersion_X1a:
case PDFOptions::PDFVersion_X3:
case PDFOptions::PDFVersion_13:
PutDoc("%PDF-1.3\n");
break;
case PDFOptions::PDFVersion_14:
PutDoc("%PDF-1.4\n");
break;
case PDFOptions::PDFVersion_X4:
case PDFOptions::PDFVersion_15:
PutDoc("%PDF-1.5\n");
break;
writer.MetaDataObj = writer.newObject();
PutDoc("/Metadata "+ Pdf::toObjRef(writer.MetaDataObj) + "\n");
}
if (PDF_IsPDFX())
ObjCounter++;
PutDoc("%\xc7\xec\x8f\xa2\n");
StartObj(1);
PutDoc("<<\n/Type /Catalog\n/Outlines 3 0 R\n/Pages 4 0 R\n/Dests 5 0 R\n/AcroForm 6 0 R\n/Names 7 0 R\n/Threads 8 0 R\n");
if (((Options.Version == PDFOptions::PDFVersion_15) || (Options.Version == PDFOptions::PDFVersion_X4)) && (Options.useLayers))
PutDoc("/OCProperties 9 0 R\n");
if (PDF_IsPDFX())
PutDoc("/OutputIntents [ "+QString::number(ObjCounter-1)+" 0 R ]\n");
if ((Options.Version == PDFOptions::PDFVersion_X4))
{
ObjCounter++;
PutDoc("/Metadata "+QString::number(ObjCounter-1)+" 0 R\n");
}
PutDoc("/PageLayout ");
switch (Options.PageLayout)
{
849,24 → 707,25
PutDoc("/PageMode /UseOC\n");
if (!Options.openAction.isEmpty())
{
PutDoc("/OpenAction << /S /JavaScript /JS (this."+Options.openAction+"\\(\\)) >>\n");
PutDoc("/OpenAction << /S /JavaScript /JS (this."+Pdf::toPdfDocEncoding(Options.openAction)+"\\(\\)) >>\n");
}
QString tmp;
QDateTime dt = QDateTime::currentDateTime().toUTC();
QDate d = dt.date();
Datum = "D:";
tmp.sprintf("%4d", d.year());
tmp.replace(QRegExp(" "), "0");
Datum += tmp;
tmp.sprintf("%2d", d.month());
tmp.replace(QRegExp(" "), "0");
Datum += tmp;
tmp.sprintf("%2d", d.day());
tmp.replace(QRegExp(" "), "0");
Datum += tmp;
tmp = dt.time().toString();
tmp.replace(QRegExp(":"), "");
Datum += tmp;
Datum += "Z";
Datum = Pdf::toDateString(dt);
// "D:";
// tmp.sprintf("%4d", d.year());
// tmp.replace(QRegExp(" "), "0");
// Datum += tmp;
// tmp.sprintf("%2d", d.month());
// tmp.replace(QRegExp(" "), "0");
// Datum += tmp;
// tmp.sprintf("%2d", d.day());
// tmp.replace(QRegExp(" "), "0");
// Datum += tmp;
// tmp = dt.time().toString();
// tmp.replace(QRegExp(":"), "");
// Datum += tmp;
// Datum += "Z";
 
// only include XMP to PDF/X-4 at the moment, could easily be extended to include it to any PDF
if (Options.Version == PDFOptions::PDFVersion_X4)
908,7 → 767,12
PutDoc("/HideMenubar true\n");
if (Options.fitWindow)
PutDoc("/FitWindow true\n");
PutDoc(" >>\n>>\nendobj\n");
PutDoc(" >>\n>>");
writer.endObj(writer.CatalogObj);
}
 
void PDFLibCore::PDF_Begin_MetadataAndEncrypt()
{
QString IDg(Datum);
IDg += Options.fileName;
IDg += "Scribus "+QString(VERSION);
916,43 → 780,24
IDg += doc.documentInfo().title();
IDg += doc.documentInfo().author();
IDg += "/False";
FileID = ComputeMD5(IDg);
writer.setFileId(Pdf::toPdfDocEncoding(IDg));
if (Options.Encrypt)
{
if ((Options.Version == PDFOptions::PDFVersion_14) || (Options.Version == PDFOptions::PDFVersion_15))
KeyLen = 16;
else
KeyLen = 5;
CalcOwnerKey(Options.PassOwner, Options.PassUser);
CalcUserKey(Options.PassUser, Options.Permissions);
for (uint cl2 = 0; cl2 < 32; ++cl2)
ok += QChar(OwnerKey[cl2]);
if (KeyLen > 5)
{
for (uint cl3 = 0; cl3 < 16; ++cl3)
uk += QChar(UserKey[cl3]);
for (uint cl3r = 0; cl3r < 16; ++cl3r)
uk += QChar(KeyGen[cl3r]);
}
else
{
for (uint cl = 0; cl < 32; ++cl)
uk += QChar(UserKey[cl]);
}
writer.setEncryption((Options.Version == PDFOptions::PDFVersion_14) || (Options.Version == PDFOptions::PDFVersion_15), Pdf::toPdfDocEncoding(Options.PassOwner), Pdf::toPdfDocEncoding(Options.PassUser), Options.Permissions);
}
StartObj(2);
PutDoc("<<\n/Creator " + EncString("Scribus "+QString(VERSION), 2) + "\n");
PutDoc("/Producer " + EncString("Scribus PDF Library "+QString(VERSION), 2) + "\n");
writer.startObj(writer.InfoObj);
PutDoc("<<\n/Creator " + EncString(QByteArray("Scribus ") + VERSION, writer.InfoObj) + "\n");
PutDoc("/Producer " + EncString(QByteArray("Scribus PDF Library ") + VERSION, writer.InfoObj) + "\n");
QString docTitle = doc.documentInfo().title();
if ((PDF_IsPDFX()) && (docTitle.isEmpty()))
PutDoc("/Title " + EncStringUTF16(doc.DocName, 2) + "\n");
PutDoc("/Title " + EncStringUTF16(doc.DocName, writer.InfoObj) + "\n");
else
PutDoc("/Title " + EncStringUTF16(doc.documentInfo().title(), 2) + "\n");
PutDoc("/Author " + EncStringUTF16(doc.documentInfo().author(), 2) + "\n");
PutDoc("/Subject " + EncStringUTF16(doc.documentInfo().subject(), 2) + "\n");
PutDoc("/Keywords " + EncStringUTF16(doc.documentInfo().keywords(), 2) + "\n");
PutDoc("/CreationDate " + EncString(Datum, 2) + "\n");
PutDoc("/ModDate " + EncString(Datum, 2) + "\n");
PutDoc("/Title " + EncStringUTF16(doc.documentInfo().title(), writer.InfoObj) + "\n");
PutDoc("/Author " + EncStringUTF16(doc.documentInfo().author(), writer.InfoObj) + "\n");
PutDoc("/Subject " + EncStringUTF16(doc.documentInfo().subject(), writer.InfoObj) + "\n");
PutDoc("/Keywords " + EncStringUTF16(doc.documentInfo().keywords(), writer.InfoObj) + "\n");
PutDoc("/CreationDate " + EncString(Datum, writer.InfoObj) + "\n");
PutDoc("/ModDate " + EncString(Datum, writer.InfoObj) + "\n");
if (Options.Version == PDFOptions::PDFVersion_X1a)
{
PutDoc("/GTS_PDFXVersion (PDF/X-1:2001)\n");
962,30 → 807,40
PutDoc("/GTS_PDFXVersion (PDF/X-3:2002)\n");
if (Options.Version == PDFOptions::PDFVersion_X4)
PutDoc("/GTS_PDFXVersion (PDF/X-4)\n");
PutDoc("/Trapped /False\n>>\nendobj\n");
for (int t = 0; t < 6; ++t)
XRef.append(bytesWritten());
if (((Options.Version == PDFOptions::PDFVersion_15) || (Options.Version == PDFOptions::PDFVersion_X4)) && (Options.useLayers))
XRef.append(bytesWritten());
if (PDF_IsPDFX())
XRef.append(bytesWritten());
if (Options.Version == PDFOptions::PDFVersion_X4)
XRef.append(bytesWritten());
PutDoc("/Trapped /False\n>>");
writer.endObj(writer.InfoObj);
 
// Encrypt
// for (int t = 0; t < 6; ++t)
// XRef.append(bytesWritten());
// if (((Options.Version == PDFOptions::PDFVersion_15) || (Options.Version == PDFOptions::PDFVersion_X4)) && (Options.useLayers))
// XRef.append(bytesWritten());
// if (PDF_IsPDFX())
// XRef.append(bytesWritten());
// if (Options.Version == PDFOptions::PDFVersion_X4)
// XRef.append(bytesWritten());
if (Options.Encrypt)
{
Encrypt = newObject();
StartObj(Encrypt);
PutDoc("<<\n/Filter /Standard\n");
PutDoc( KeyLen > 5 ? "/R 3\n/V 2\n/Length 128\n" : "/R 2\n/V 1\n");
PutDoc("/O <"+String2Hex(&ok)+">\n");
PutDoc("/U <"+String2Hex(&uk)+">\n");
PutDoc("/P "+QString::number(Options.Permissions)+"\n>>\nendobj\n");
{ // now done in writer.setEncrption():
// writer.EncryptObj = writer.newObject();
// writer.startObj(writer.EncryptObj);
// PutDoc("<<\n/Filter /Standard\n");
// PutDoc( KeyLen > 5 ? "/R 3\n/V 2\n/Length 128\n" : "/R 2\n/V 1\n");
// PutDoc("/O "+Pdf::toHexString(ok)+"\n");
// PutDoc("/U "+Pdf::toHexString(uk)+"\n");
// PutDoc("/P "+Pdf::toPdf(Options.Permissions)+"\n>>");
// writer.endObj(writer.EncryptObj);
}
}
 
QMap<QString, QMap<uint, FPointArray> >
PDFLibCore::PDF_Begin_FindUsedFonts(SCFonts &AllFonts, const QMap<QString, QMap<uint, FPointArray> >& DocFonts)
{
QMap<QString, QMap<uint, FPointArray> > ReallyUsed;
ReallyUsed.clear();
PageItem* pgit;
QMap<int, QString> ind2PDFabr;
const QString tmpf[] = {"/Courier", "/Courier-Bold", "/Courier-Oblique", "/Courier-BoldOblique",
QMap<int, QByteArray> ind2PDFabr;
const QByteArray tmpf[] = {"/Courier", "/Courier-Bold", "/Courier-Oblique", "/Courier-BoldOblique",
"/Helvetica", "/Helvetica-Bold", "/Helvetica-Oblique", "/Helvetica-BoldOblique",
"/Times-Roman", "/Times-Bold", "/Times-Italic", "/Times-BoldItalic",
"/ZapfDingbats", "/Symbol"};
1208,647 → 1063,957
}
}
}
a = 0;
QMap<QString, QString>::Iterator itStd;
for (itStd = StdFonts.begin(); itStd != StdFonts.end(); ++itStd)
{
uint fontObject = newObject();
StartObj(fontObject);
PutDoc("<<\n/Type /Font\n/Subtype /Type1\n");
PutDoc("/Name /FoStd"+QString::number(a)+"\n");
PutDoc("/BaseFont "+itStd.key()+"\n");
if (itStd.key() != "/ZapfDingbats")
{
PutDoc("/Encoding << \n");
PutDoc("/Differences [ \n");
PutDoc("24 /breve /caron /circumflex /dotaccent /hungarumlaut /ogonek /ring /tilde\n");
PutDoc("39 /quotesingle 96 /grave 128 /bullet /dagger /daggerdbl /ellipsis /emdash /endash /florin /fraction /guilsinglleft /guilsinglright\n");
PutDoc("/minus /perthousand /quotedblbase /quotedblleft /quotedblright /quoteleft /quoteright /quotesinglbase /trademark /fi /fl /Lslash /OE /Scaron\n");
PutDoc("/Ydieresis /Zcaron /dotlessi /lslash /oe /scaron /zcaron 164 /currency 166 /brokenbar 168 /dieresis /copyright /ordfeminine 172 /logicalnot\n");
PutDoc("/.notdef /registered /macron /degree /plusminus /twosuperior /threesuperior /acute /mu 183 /periodcentered /cedilla /onesuperior /ordmasculine\n");
PutDoc("188 /onequarter /onehalf /threequarters 192 /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla /Egrave /Eacute /Ecircumflex\n");
PutDoc("/Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash\n");
PutDoc("/Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla\n");
PutDoc("/egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis\n");
PutDoc("/divide /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis\n");
PutDoc("] >>\n");
}
PutDoc(">>\nendobj\n");
Seite.FObjects["FoStd"+QString::number(a)] = fontObject;
itStd.value() = "FoStd"+QString::number(a);
a++;
}
QMap<QString,QMap<uint, FPointArray> >::Iterator it;
a = 0;
for (it = ReallyUsed.begin(); it != ReallyUsed.end(); ++it)
{
ScFace& face(AllFonts[it.key()]);
ScFace::FontFormat fformat = face.format();
if (Options.OutlineList.contains(it.key()))
{
// Paint glyphs with XForm objects
QString fon("");
QMap<uint,FPointArray>& RealGlyphs(it.value());
QMap<uint,FPointArray>::Iterator ig;
for (ig = RealGlyphs.begin(); ig != RealGlyphs.end(); ++ig)
{
FPoint np, np1, np2;
bool nPath = true;
fon = "";
if (ig.value().size() > 3)
{
FPointArray gly = ig.value();
QTransform mat;
mat.scale(0.1, 0.1);
gly.map(mat);
for (int poi = 0; poi < gly.size()-3; poi += 4)
{
if (gly.isMarker(poi))
{
fon += "h\n";
nPath = true;
continue;
}
if (nPath)
{
np = gly.point(poi);
fon += FToStr(np.x())+" "+FToStr(-np.y())+" m\n";
nPath = false;
}
np = gly.point(poi+1);
np1 = gly.point(poi+3);
np2 = gly.point(poi+2);
fon += FToStr(np.x()) + " " + FToStr(-np.y()) + " " +
FToStr(np1.x()) + " " + FToStr(-np1.y()) + " " +
FToStr(np2.x()) + " " + FToStr(-np2.y()) + " c\n";
}
fon += "h f*\n";
np = getMinClipF(&gly);
np1 = getMaxClipF(&gly);
}
else
{
fon = "h";
np = FPoint(0, 0);
np1 = FPoint(0, 0);
}
uint fontGlyphXForm = newObject();
StartObj(fontGlyphXForm);
PutDoc("<<\n/Type /XObject\n/Subtype /Form\n/FormType 1\n");
PutDoc("/BBox [ "+FToStr(np.x())+" "+FToStr(-np.y())+" "+FToStr(np1.x())+ " "+FToStr(-np1.y())+" ]\n");
PutDoc("/Resources << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]\n");
PutDoc(">>\n");
if (Options.Compress)
fon = CompressStr(&fon);
PutDoc("/Length "+QString::number(fon.length()+1));
if (Options.Compress)
PutDoc("\n/Filter /FlateDecode");
PutDoc(" >>\nstream\n"+EncStream(fon, fontGlyphXForm)+"\nendstream\nendobj\n");
Seite.XObjects[face.psName().replace( QRegExp("[\\s\\/\\{\\[\\]\\}\\<\\>\\(\\)\\%]"), "_" )+QString::number(ig.key())] = fontGlyphXForm;
}
}
else if (Options.SubsetList.contains(it.key()))
{
// use PDF Type3 font
UsedFontsP.insert(it.key(), "/Fo"+QString::number(a));
uint SubFonts = 0;
int glyphCount = 0;
double minx = std::numeric_limits<double>::max();
double miny = std::numeric_limits<double>::max();
double maxx = -std::numeric_limits<double>::max();
double maxy = -std::numeric_limits<double>::max();
QList<uint> glyphWidths;
QStringList charProcs;
QString encoding = "<< /Type /Encoding\n/Differences [ 0\n";
QString fon("");
QMap<uint, uint> glyphMapping;
QMap<uint,std::pair<QChar,QString> > gl;
face.glyphNames(gl);
QMap<uint,FPointArray>& RealGlyphs(it.value());
QMap<uint,FPointArray>::Iterator ig;
for (ig = RealGlyphs.begin(); ig != RealGlyphs.end(); ++ig)
{
FPoint np, np1, np2;
bool nPath = true;
fon = "";
if (ig.value().size() > 3)
{
FPointArray gly = ig.value();
QTransform mat;
mat.scale(100.0, -100.0);
gly.map(mat);
gly.translate(0, 1000);
for (int poi = 0; poi < gly.size()-3; poi += 4)
{
if (gly.isMarker(poi))
{
fon += "h\n";
nPath = true;
continue;
}
if (nPath)
{
np = gly.point(poi);
fon += FToStr(np.x())+" "+FToStr(np.y())+" m\n";
nPath = false;
}
np = gly.point(poi+1);
np1 = gly.point(poi+3);
np2 = gly.point(poi+2);
fon += FToStr(np.x()) + " " + FToStr(np.y()) + " " + FToStr(np1.x()) + " " + FToStr(np1.y()) + " " + FToStr(np2.x()) + " " + FToStr(np2.y()) + " c\n";
}
fon += "h f*\n";
np = getMinClipF(&gly);
np1 = getMaxClipF(&gly);
}
else
{
fon = "h";
np = FPoint(0, 0);
np1 = FPoint(0, 0);
}
fon.prepend(QString::number(qRound(np1.x())) + " 0 "+QString::number(qRound(np.x()))+" "+QString::number(qRound(np.y()))+" "+QString::number(qRound(np1.x()))+ " "+QString::number(qRound(np1.y()))+" d1\n");
minx = qMin(minx, np.x());
miny = qMin(miny, np.y());
maxx = qMax(maxx, np1.x());
maxy = qMax(maxy, np1.y());
glyphWidths.append(qRound(np1.x()));
uint charProcObject = newObject();
charProcs.append("/"+gl[ig.key()].second+" "+QString::number(charProcObject)+" 0 R\n");
encoding += "/"+gl[ig.key()].second+" ";
glyphMapping.insert(ig.key(), glyphCount + SubFonts * 256);
StartObj(charProcObject);
if (Options.Compress)
fon = CompressStr(&fon);
PutDoc("<< /Length "+QString::number(fon.length()+1));
if (Options.Compress)
PutDoc("\n/Filter /FlateDecode");
PutDoc("\n>>\nstream\n"+EncStream(fon, charProcObject)+"\nendstream\nendobj\n");
glyphCount++;
int glyphsLeft = RealGlyphs.count() - SubFonts * 256;
if ((glyphCount > 255) || (glyphCount == glyphsLeft))
{
uint fontWidths = newObject();
StartObj(fontWidths);
PutDoc("[ ");
for (int ww = 0; ww < glyphWidths.count(); ++ww)
{
PutDoc(QString::number(glyphWidths[ww])+" ");
}
PutDoc("]\nendobj\n");
uint fontCharProcs = newObject();
StartObj(fontCharProcs);
PutDoc("<<\n");
for (int ww = 0; ww < charProcs.count(); ++ww)
{
PutDoc(charProcs[ww]);
}
PutDoc(">>\nendobj\n");
uint fontEncoding = newObject();
StartObj(fontEncoding);
PutDoc(encoding);
PutDoc("]\n");
PutDoc(">>\nendobj\n");
uint font3Object = newObject();
StartObj(font3Object);
PutDoc("<<\n/Type /Font\n/Subtype /Type3\n");
PutDoc("/Name /Fo"+QString::number(a)+"S"+QString::number(SubFonts)+"\n");
PutDoc("/FirstChar 0\n");
PutDoc("/LastChar "+QString::number(glyphCount-1)+"\n");
PutDoc("/Widths "+QString::number(fontWidths)+" 0 R\n");
PutDoc("/CharProcs "+QString::number(fontCharProcs)+" 0 R\n");
PutDoc("/FontBBox ["+QString::number(qRound(minx))+" "+QString::number(qRound(miny))+" "+QString::number(qRound(maxx))+ " "+QString::number(qRound(maxy))+"]\n");
PutDoc("/FontMatrix [0.001 0 0 0.001 0 0]\n");
PutDoc("/Encoding "+QString::number(fontEncoding)+" 0 R\n");
PutDoc(">>\nendobj\n");
Seite.FObjects["Fo"+QString::number(a)+"S"+QString::number(SubFonts)] = font3Object;
charProcs.clear();
glyphWidths.clear();
// glyphMapping.clear();
glyphCount = 0;
++SubFonts;
minx = std::numeric_limits<double>::max();
miny = std::numeric_limits<double>::max();
maxx = -std::numeric_limits<double>::max();
maxy = -std::numeric_limits<double>::max();
encoding = "<< /Type /Encoding\n/Differences [ 0\n";
}
}
Type3Fonts.insert("/Fo"+QString::number(a), glyphMapping);
}
else
{
UsedFontsP.insert(it.key(), "/Fo"+QString::number(a));
uint embeddedFontObject = 0;
if ((fformat == ScFace::PFB) && (Options.EmbedList.contains(it.key())))
{
QString fon("");
QByteArray bb;
embeddedFontObject = newObject();
StartObj(embeddedFontObject);
face.RawData(bb);
int posi;
for (posi = 6; posi < bb.size(); ++posi)
{
if ((bb[posi] == static_cast<char>(0x80)) && (static_cast<int>(bb[posi+1]) == 2))
break;
fon += QChar(bb[posi]);
}
int len1 = fon.length();
int ulen;
ulen = bb[posi+2] & 0xff;
ulen |= (bb[posi+3] << 8) & 0xff00;
ulen |= (bb[posi+4] << 16) & 0xff0000;
ulen |= (bb[posi+5] << 24) & 0xff000000;
if (ulen > bb.size())
ulen = bb.size()-7;
posi += 6;
for (int j = 0; j < ulen; ++j)
fon += QChar(bb[posi++]);
posi += 6;
int len2 = fon.length()-len1;
for (int j = posi; j < bb.size(); ++j)
{
if ((bb[j] == static_cast<char>(0x80)) && (static_cast<int>(bb[j+1]) == 3))
break;
if (bb[j] == '\r')
fon += "\n";
else
fon += QChar(bb[j]);
}
int len3 = fon.length()-len2-len1;
if (Options.Compress)
fon = CompressStr(&fon);
PutDoc("<<\n/Length "+QString::number(fon.length()+1)+"\n");
PutDoc("/Length1 "+QString::number(len1)+"\n");
PutDoc("/Length2 "+QString::number(len2)+"\n");
PutDoc("/Length3 "+QString::number(len3)+"\n");
if (Options.Compress)
PutDoc("/Filter /FlateDecode\n");
PutDoc(">>\nstream\n"+EncStream(fon,embeddedFontObject)+"\nendstream\nendobj\n");
}
if ((fformat == ScFace::PFA) && (Options.EmbedList.contains(it.key())))
{
QString fon("");
QString fon2("");
QString tm("");
uint value;
bool ok = true;
embeddedFontObject = newObject();
StartObj(embeddedFontObject);
face.EmbedFont(fon);
int len1 = fon.indexOf("eexec")+5;
fon2 = fon.left(len1)+"\n";
int len2 = fon.indexOf("0000000000000000000000000");
if (len2 == -1)
len2 = fon.length()+1;
int count = 0;
for (int xx = len1; xx < len2-1; ++xx)
{
tm = fon.at(xx);
if ((tm == QChar(13)) || (tm == QChar(10)))
continue;
xx++;
count++;
tm += fon.at(xx);
value = tm.toUInt(&ok, 16);
fon2 += QChar(value);
}
fon2 += fon.mid(len2);
if (Options.Compress)
fon2 = CompressStr(&fon2);
PutDoc("<<\n/Length "+QString::number(fon2.length()+1)+"\n");
PutDoc("/Length1 "+QString::number(len1+1)+"\n");
PutDoc("/Length2 "+QString::number(count)+"\n");
PutDoc(static_cast<int>(fon.length()-len2) == -1 ? QString("/Length3 0\n") : "/Length3 "+QString::number(fon.length()-len2)+"\n");
if (Options.Compress)
PutDoc("/Filter /FlateDecode\n");
PutDoc(">>\nstream\n"+EncStream(fon2, embeddedFontObject)+"\nendstream\nendobj\n");
}
if ((fformat == ScFace::SFNT || fformat == ScFace::TTCF) && (Options.EmbedList.contains(it.key())))
{
QByteArray bb;
embeddedFontObject = newObject();
StartObj(embeddedFontObject);
face.RawData(bb);
int len = bb.length();
if (Options.Compress)
bb = CompressArray(bb);
//qDebug() << QString("sfnt data: size=%1 compressed=%2").arg(len).arg(bb.length());
PutDoc("<<\n/Length " + QString::number(bb.length() + 1) + "\n");
PutDoc("/Length1 " + QString::number(len) + "\n");
if (Options.Compress)
PutDoc("/Filter /FlateDecode\n");
PutDoc(">>\nstream\n");
EncodeArrayToStream(bb, embeddedFontObject);
PutDoc("\nendstream\nendobj\n");
}
uint fontDescriptor = newObject();
StartObj(fontDescriptor);
// TODO: think about QByteArray ScFace::getFontDescriptor() -- AV
PutDoc("<<\n/Type /FontDescriptor\n");
PutDoc("/FontName /" + face.psName().replace( QRegExp("[\\s\\/\\{\\[\\]\\}\\<\\>\\(\\)\\%]"), "_" ) + "\n");
PutDoc("/FontBBox [ " + face.pdfFontBBoxAsString() + " ]\n");
PutDoc("/Flags ");
//FIXME: isItalic() should be queried from ScFace, not from Qt -- AV
//QFontInfo fo = QFontInfo(it.data());
int pfl = 0;
if (face.isFixedPitch())
pfl = pfl ^ 1;
//if (fo.italic())
if (AllFonts[it.key()].italicAngleAsString() != "0")
pfl = pfl ^ 64;
// pfl = pfl ^ 4;
pfl = pfl ^ 32;
PutDoc(QString::number(pfl)+"\n");
PutDoc("/Ascent " + face.pdfAscentAsString()+"\n");
PutDoc("/Descent " + face.pdfDescentAsString()+"\n");
PutDoc("/CapHeight " + face.pdfCapHeightAsString()+"\n");
PutDoc("/ItalicAngle " + face.italicAngleAsString()+"\n");
PutDoc("/StemV 1\n");
if ((fformat == ScFace::SFNT || fformat == ScFace::TTCF) && (Options.EmbedList.contains(it.key())))
PutDoc("/FontFile2 "+QString::number(embeddedFontObject)+" 0 R\n");
if ((fformat == ScFace::PFB) && (Options.EmbedList.contains(it.key())))
PutDoc("/FontFile "+QString::number(embeddedFontObject)+" 0 R\n");
if ((fformat == ScFace::PFA) && (Options.EmbedList.contains(it.key())))
PutDoc("/FontFile "+QString::number(embeddedFontObject)+" 0 R\n");
PutDoc(">>\nendobj\n");
return ReallyUsed;
}
 
QMap<uint,std::pair<QChar,QString> > gl;
face.glyphNames(gl);
int nglyphs = 0;
QMap<uint,std::pair<QChar,QString> >::Iterator gli;
for (gli = gl.begin(); gli != gl.end(); ++gli)
{
if (gli.key() > static_cast<uint>(nglyphs))
nglyphs = gli.key();
}
++nglyphs;
// qDebug() << QString("pdflib: nglyphs %1 max %2").arg(nglyphs).arg(face.maxGlyph());
uint FontDes = fontDescriptor;
if ((face.isSymbolic() || !face.hasNames() || Options.Version == PDFOptions::PDFVersion_X4) &&
(fformat == ScFace::SFNT || fformat == ScFace::TTCF))
{
uint fontWidths2 = newObject();
StartObj(fontWidths2);
QStringList toUnicodeMaps;
QList<int> toUnicodeMapsCount;
QString toUnicodeMap = "";
int toUnicodeMapCounter = 0;
static QByteArray sanitizeFontName(QString fn)
{
return Pdf::toPdfDocEncoding(fn.replace( QRegExp("[\\s\\/\\{\\[\\]\\}\\<\\>\\(\\)\\%]"), "_" ));
}
 
PutDoc("[ ");
QList<uint> keys = gl.uniqueKeys();
QList<uint>::iterator git;
for (git = keys.begin(); git != keys.end(); ++git)
{
PutDoc(QString::number(*git)+" ["+QString::number(static_cast<int>(face.glyphWidth(*git)* 1000))+"] " );
QString tmp, tmp2;
tmp.sprintf("%04X", *git);
tmp2.sprintf("%04X", gl.value(*git).first.unicode());
toUnicodeMap += QString("<%1> <%2>\n").arg(tmp).arg((tmp2));
toUnicodeMapCounter++;
if (toUnicodeMapCounter == 100)
{
toUnicodeMaps.append(toUnicodeMap);
toUnicodeMapsCount.append(toUnicodeMapCounter);
toUnicodeMap = "";
toUnicodeMapCounter = 0;
}
}
PutDoc("]\nendobj\n");
if (toUnicodeMapCounter != 0)
{
toUnicodeMaps.append(toUnicodeMap);
toUnicodeMapsCount.append(toUnicodeMapCounter);
}
QString toUnicodeMapStream = "";
toUnicodeMapStream += "/CIDInit /ProcSet findresource begin\n";
toUnicodeMapStream += "12 dict begin\n";
toUnicodeMapStream += "begincmap\n";
toUnicodeMapStream += "/CIDSystemInfo <<\n";
toUnicodeMapStream += "/Registry (Adobe)\n";
toUnicodeMapStream += "/Ordering (UCS)\n";
toUnicodeMapStream += "/Supplement 0\n";
toUnicodeMapStream += ">> def\n";
toUnicodeMapStream += "/CMapName /Adobe-Identity-UCS def\n";
toUnicodeMapStream += "/CMapType 2 def\n";
toUnicodeMapStream += "1 begincodespacerange\n";
toUnicodeMapStream += "<0000> <FFFF>\n";
toUnicodeMapStream += "endcodespacerange\n";
for (int uniC = 0; uniC < toUnicodeMaps.count(); uniC++)
{
toUnicodeMapStream += QString("%1 beginbfchar\n").arg(toUnicodeMapsCount[uniC]);
toUnicodeMapStream += toUnicodeMaps[uniC];
toUnicodeMapStream += "endbfchar\n";
}
toUnicodeMapStream += "endcmap\n";
toUnicodeMapStream += "CMapName currentdict /CMap defineresource pop\n";
toUnicodeMapStream += "end\n";
toUnicodeMapStream += "end\n";
uint fontToUnicode2 = WritePDFStream(toUnicodeMapStream);
uint fontObject2 = newObject();
StartObj(fontObject2);
PutDoc("<<\n/Type /Font\n/Subtype /Type0\n");
PutDoc("/Name /Fo"+QString::number(a)+"\n");
PutDoc("/BaseFont /"+face.psName().replace( QRegExp("[\\s\\/\\{\\[\\]\\}\\<\\>\\(\\)\\%]"), "_" )+"\n");
PutDoc("/Encoding /Identity-H\n");
PutDoc("/ToUnicode "+QString::number(fontToUnicode2)+" 0 R\n");
PutDoc("/DescendantFonts [");
PutDoc("<</Type /Font");
PutDoc("/Subtype /CIDFontType2");
PutDoc("/BaseFont /"+face.psName().replace( QRegExp("[\\s\\/\\{\\[\\]\\}\\<\\>\\(\\)\\%]"), "_" ));
PutDoc("/FontDescriptor "+QString::number(FontDes)+" 0 R");
PutDoc("/CIDSystemInfo <</Ordering(Identity)/Registry(Adobe)/Supplement 0>>");
PutDoc("/DW 1000");
PutDoc("/W "+QString::number(fontWidths2)+" 0 R");
PutDoc("/CIDToGIDMap /Identity");
PutDoc(">>"); // close CIDFont dictionary
PutDoc("]\n"); // close DescendantFonts array
PutDoc(">>\nendobj\n");
Seite.FObjects["Fo"+QString::number(a)] = fontObject2;
}
else
{
uint Fcc = nglyphs / 224;
if ((nglyphs % 224) != 0)
Fcc += 1;
for (uint Fc = 0; Fc < Fcc; ++Fc)
{
uint fontWidths2 = newObject();
StartObj(fontWidths2);
int chCount = 32;
PutDoc("[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ");
for (int ww = 32; ww < 256; ++ww)
{
uint glyph = 224 * Fc + ww - 32;
if (gl.contains(glyph))
PutDoc(QString::number(static_cast<int>(face.glyphWidth(glyph)* 1000))+" ");
else
PutDoc("0 ");
chCount++;
if (signed(glyph) == nglyphs-1)
break;
}
PutDoc("]\nendobj\n");
uint fontEncoding2 = newObject();
StartObj(fontEncoding2);
QStringList toUnicodeMaps;
QList<int> toUnicodeMapsCount;
QString toUnicodeMap = "";
int toUnicodeMapCounter = 0;
PutDoc("<< /Type /Encoding\n");
PutDoc("/Differences [ \n");
int crc = 0;
bool startOfSeq = true;
for (int ww2 = 32; ww2 < 256; ++ww2)
{
uint glyph = 224 * Fc + ww2 - 32;
QMap<uint,std::pair<QChar,QString> >::Iterator glIt = gl.find(glyph);
if (glIt != gl.end() && !glIt.value().second.isEmpty())
{
if (startOfSeq)
{
PutDoc(QString::number(ww2)+" ");
startOfSeq = false;
}
PutDoc("/"+glIt.value().second+" ");
QString tmp, tmp2;
tmp.sprintf("%02X", ww2);
tmp2.sprintf("%04X", glIt.value().first.unicode());
toUnicodeMap += QString("<%1> <%2>\n").arg(tmp).arg((tmp2));
toUnicodeMapCounter++;
if (toUnicodeMapCounter == 100)
{
toUnicodeMaps.append(toUnicodeMap);
toUnicodeMapsCount.append(toUnicodeMapCounter);
toUnicodeMap = "";
toUnicodeMapCounter = 0;
}
crc++;
}
else
{
startOfSeq = true;
}
if (signed(glyph) == nglyphs-1)
break;
if (crc > 8)
{
PutDoc("\n");
crc = 0;
}
}
if (toUnicodeMapCounter != 0)
{
toUnicodeMaps.append(toUnicodeMap);
toUnicodeMapsCount.append(toUnicodeMapCounter);
}
PutDoc("]\n");
PutDoc(">>\nendobj\n");
QString toUnicodeMapStream = "";
toUnicodeMapStream += "/CIDInit /ProcSet findresource begin\n";
toUnicodeMapStream += "12 dict begin\n";
toUnicodeMapStream += "begincmap\n";
toUnicodeMapStream += "/CIDSystemInfo <<\n";
toUnicodeMapStream += "/Registry (Adobe)\n";
toUnicodeMapStream += "/Ordering (UCS)\n";
toUnicodeMapStream += "/Supplement 0\n";
toUnicodeMapStream += ">> def\n";
toUnicodeMapStream += "/CMapName /Adobe-Identity-UCS def\n";
toUnicodeMapStream += "/CMapType 2 def\n";
toUnicodeMapStream += "1 begincodespacerange\n";
toUnicodeMapStream += "<0000> <FFFF>\n";
toUnicodeMapStream += "endcodespacerange\n";
for (int uniC = 0; uniC < toUnicodeMaps.count(); uniC++)
{
toUnicodeMapStream += QString("%1 beginbfchar\n").arg(toUnicodeMapsCount[uniC]);
toUnicodeMapStream += toUnicodeMaps[uniC];
toUnicodeMapStream += "endbfchar\n";
}
toUnicodeMapStream += "endcmap\n";
toUnicodeMapStream += "CMapName currentdict /CMap defineresource pop\n";
toUnicodeMapStream += "end\n";
toUnicodeMapStream += "end\n";
uint fontToUnicode2 = WritePDFStream(toUnicodeMapStream);
uint fontObject2 = newObject();
StartObj(fontObject2);
PutDoc("<<\n/Type /Font\n/Subtype ");
PutDoc((fformat == ScFace::SFNT || fformat == ScFace::TTCF) ? "/TrueType\n" : "/Type1\n");
PutDoc("/Name /Fo"+QString::number(a)+"S"+QString::number(Fc)+"\n");
PutDoc("/BaseFont /"+face.psName().replace( QRegExp("[\\s\\/\\{\\[\\]\\}\\<\\>\\(\\)\\%]"), "_" )+"\n");
PutDoc("/FirstChar 0\n");
PutDoc("/LastChar "+QString::number(chCount-1)+"\n");
PutDoc("/Widths "+QString::number(fontWidths2)+" 0 R\n");
PutDoc("/Encoding "+QString::number(fontEncoding2)+" 0 R\n");
PutDoc("/ToUnicode "+QString::number(fontToUnicode2)+" 0 R\n");
PutDoc("/FontDescriptor "+QString::number(FontDes)+" 0 R\n");
PutDoc(">>\nendobj\n");
Seite.FObjects["Fo"+QString::number(a)+"S"+QString::number(Fc)] = fontObject2;
} // for(Fc)
uint fontWidthsForm = newObject();
StartObj(fontWidthsForm);
PutDoc("[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ");
for (int ww = 32; ww < 256; ++ww)
{
uint glyph = face.char2CMap(QChar(ww));
if (gl.contains(glyph))
PutDoc(QString::number(static_cast<int>(face.glyphWidth(glyph)* 1000))+" ");
else
PutDoc("0 ");
}
PutDoc("]\nendobj\n");
uint fontObjectForm = newObject();
StartObj(fontObjectForm);
PutDoc("<<\n/Type /Font\n/Subtype ");
PutDoc((fformat == ScFace::SFNT || fformat == ScFace::TTCF) ? "/TrueType\n" : "/Type1\n");
// if (fformat == ScFace::SFNT || fformat == ScFace::TTCF)
// {
// PutDoc("/TrueType\n");
PutDoc("/Name /Fo"+QString::number(a)+"Form"+"\n");
Seite.FObjects["Fo"+QString::number(a)+"Form"] = fontObjectForm;
UsedFontsF.insert(it.key(), "/Fo"+QString::number(a)+"Form");
/* }
else
{
PutDoc("/Type1\n");
PutDoc("/Name /"+face.psName().replace( QRegExp("[\\s\\/\\{\\[\\]\\}\\<\\>\\(\\)\\%]"), "_" )+"\n");
Seite.FObjects[face.psName().replace( QRegExp("[\\s\\/\\{\\[\\]\\}\\<\\>\\(\\)\\%]"), "_" )] = ObjCounter;
UsedFontsF.insert(it.key(), "/"+face.psName().replace( QRegExp("[\\s\\/\\{\\[\\]\\}\\<\\>\\(\\)\\%]"), "_" ));
} */
PutDoc("/BaseFont /"+face.psName().replace( QRegExp("[\\s\\/\\{\\[\\]\\}\\<\\>\\(\\)\\%]"), "_" )+"\n");
PutDoc("/Encoding << \n");
PutDoc("/Differences [ \n");
PutDoc("24 /breve /caron /circumflex /dotaccent /hungarumlaut /ogonek /ring /tilde\n");
PutDoc("39 /quotesingle 96 /grave 128 /bullet /dagger /daggerdbl /ellipsis /emdash /endash /florin /fraction /guilsinglleft /guilsinglright\n");
PutDoc("/minus /perthousand /quotedblbase /quotedblleft /quotedblright /quoteleft /quoteright /quotesinglbase /trademark /fi /fl /Lslash /OE /Scaron\n");
PutDoc("/Ydieresis /Zcaron /dotlessi /lslash /oe /scaron /zcaron 164 /currency 166 /brokenbar 168 /dieresis /copyright /ordfeminine 172 /logicalnot\n");
PutDoc("/.notdef /registered /macron /degree /plusminus /twosuperior /threesuperior /acute /mu 183 /periodcentered /cedilla /onesuperior /ordmasculine\n");
PutDoc("188 /onequarter /onehalf /threequarters 192 /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla /Egrave /Eacute /Ecircumflex\n");
PutDoc("/Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash\n");
PutDoc("/Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla\n");
PutDoc("/egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis\n");
PutDoc("/divide /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis\n");
PutDoc("] >>\n");
PutDoc("/FirstChar 0\n");
PutDoc("/LastChar 255\n");
PutDoc("/Widths "+QString::number(fontWidthsForm)+" 0 R\n");
PutDoc("/FontDescriptor "+QString::number(FontDes)+" 0 R\n");
PutDoc(">>\nendobj\n");
}
}
a++;
}
static QList<Pdf::Resource> asColorSpace(QList<PdfICCD> iccCSlist)
{
QList<Pdf::Resource> result;
foreach (Pdf::Resource r, iccCSlist)
{
result.append(r);
}
return result;
}
 
static QList<Pdf::Resource> asColorSpace(QList<PdfSpotC> spotMapValues)
{
QList<Pdf::Resource> result;
foreach (Pdf::Resource r, spotMapValues)
{
result.append(r);
}
return result;
}
 
 
void PDFLibCore::PDF_WriteStandardFonts()
{
int a = 0;
QMap<QString, QString>::Iterator itStd;
for (itStd = StdFonts.begin(); itStd != StdFonts.end(); ++itStd)
{
PdfId fontObject = writer.newObject();
writer.startObj(fontObject);
PutDoc("<<\n/Type /Font\n/Subtype /Type1\n");
PutDoc("/Name /FoStd"+Pdf::toPdf(a)+"\n");
PutDoc("/BaseFont "+Pdf::toPdfDocEncoding(itStd.key())+"\n");
if (itStd.key() != "/ZapfDingbats")
{
PutDoc("/Encoding << \n");
PutDoc("/Differences [ \n");
PutDoc("24 /breve /caron /circumflex /dotaccent /hungarumlaut /ogonek /ring /tilde\n");
PutDoc("39 /quotesingle 96 /grave 128 /bullet /dagger /daggerdbl /ellipsis /emdash /endash /florin /fraction /guilsinglleft /guilsinglright\n");
PutDoc("/minus /perthousand /quotedblbase /quotedblleft /quotedblright /quoteleft /quoteright /quotesinglbase /trademark /fi /fl /Lslash /OE /Scaron\n");
PutDoc("/Ydieresis /Zcaron /dotlessi /lslash /oe /scaron /zcaron 164 /currency 166 /brokenbar 168 /dieresis /copyright /ordfeminine 172 /logicalnot\n");
PutDoc("/.notdef /registered /macron /degree /plusminus /twosuperior /threesuperior /acute /mu 183 /periodcentered /cedilla /onesuperior /ordmasculine\n");
PutDoc("188 /onequarter /onehalf /threequarters 192 /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla /Egrave /Eacute /Ecircumflex\n");
PutDoc("/Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash\n");
PutDoc("/Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla\n");
PutDoc("/egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis\n");
PutDoc("/divide /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis\n");
PutDoc("] >>\n");
}
PutDoc(">>");
writer.endObj(fontObject);
pageData.FObjects["FoStd"+Pdf::toPdf(a)] = fontObject;
itStd.value() = "FoStd"+Pdf::toPdf(a);
a++;
}
}
 
 
PdfFont PDFLibCore::PDF_WriteType3Font(const QByteArray& name, ScFace& face, const QMap<uint,FPointArray>& RealGlyphs)
{
PdfFont result;
result.name = Pdf::toName(name);
result.method = Use_Type3;
result.encoding = Encode_256;
 
uint SubFonts = 0;
int glyphCount = 0;
double minx = std::numeric_limits<double>::max();
double miny = std::numeric_limits<double>::max();
double maxx = -std::numeric_limits<double>::max();
double maxy = -std::numeric_limits<double>::max();
QList<uint> glyphWidths;
QList<QByteArray> charProcs;
QByteArray encoding = "<< /Type /Encoding\n/Differences [ 0\n";
QByteArray fon;
QMap<uint, uint> glyphMapping;
ScFace::FaceEncoding gl;
face.glyphNames(gl);
QMap<uint,FPointArray>::ConstIterator ig;
for (ig = RealGlyphs.cbegin(); ig != RealGlyphs.cend(); ++ig)
{
FPoint np, np1, np2;
bool nPath = true;
fon.resize(0);
if (ig.value().size() > 3)
{
FPointArray gly = ig.value();
QTransform mat;
mat.scale(100.0, -100.0);
gly.map(mat);
gly.translate(0, 1000);
for (int poi = 0; poi < gly.size()-3; poi += 4)
{
if (gly.isMarker(poi))
{
fon += "h\n";
nPath = true;
continue;
}
if (nPath)
{
np = gly.point(poi);
fon += FToStr(np.x())+" "+FToStr(np.y())+" m\n";
nPath = false;
}
np = gly.point(poi+1);
np1 = gly.point(poi+3);
np2 = gly.point(poi+2);
fon += FToStr(np.x()) + " " + FToStr(np.y()) + " " + FToStr(np1.x()) + " " + FToStr(np1.y()) + " " + FToStr(np2.x()) + " " + FToStr(np2.y()) + " c\n";
}
fon += "h f*\n";
np = getMinClipF(&gly);
np1 = getMaxClipF(&gly);
}
else
{
fon = "h";
np = FPoint(0, 0);
np1 = FPoint(0, 0);
}
fon.prepend(Pdf::toPdf(qRound(np1.x())) + " 0 "+Pdf::toPdf(qRound(np.x()))+" "+Pdf::toPdf(qRound(np.y()))+" "+Pdf::toPdf(qRound(np1.x()))+ " "+Pdf::toPdf(qRound(np1.y()))+" d1\n");
minx = qMin(minx, np.x());
miny = qMin(miny, np.y());
maxx = qMax(maxx, np1.x());
maxy = qMax(maxy, np1.y());
glyphWidths.append(qRound(np1.x()));
PdfId charProcObject = writer.newObject();
charProcs.append(Pdf::toName(gl[ig.key()].second)+" "+Pdf::toPdf(charProcObject)+" 0 R\n");
encoding += Pdf::toName(gl[ig.key()].second)+" ";
glyphMapping.insert(ig.key(), glyphCount + SubFonts * 256);
writer.startObj(charProcObject);
if (Options.Compress)
fon = CompressArray(fon);
PutDoc("<< /Length "+Pdf::toPdf(fon.length()+1));
if (Options.Compress)
PutDoc("\n/Filter /FlateDecode");
PutDoc("\n>>\nstream\n"+EncStream(fon, charProcObject)+"\nendstream");
writer.endObj(charProcObject);
glyphCount++;
int glyphsLeft = RealGlyphs.count() - SubFonts * 256;
if ((glyphCount > 255) || (glyphCount == glyphsLeft))
{
PdfId fontWidths = writer.newObject();
writer.startObj(fontWidths);
PutDoc("[ ");
for (int ww = 0; ww < glyphWidths.count(); ++ww)
{
PutDoc(Pdf::toPdf(glyphWidths[ww])+" ");
}
PutDoc("]");
writer.endObj(fontWidths);
PdfId fontCharProcs = writer.newObject();
writer.startObj(fontCharProcs);
PutDoc("<<\n");
for (int ww = 0; ww < charProcs.count(); ++ww)
{
PutDoc(charProcs[ww]);
}
PutDoc(">>");
writer.endObj(fontCharProcs);
PdfId fontEncoding = writer.newObject();
writer.startObj(fontEncoding);
PutDoc(encoding);
PutDoc("]\n");
PutDoc(">>");
writer.endObj(fontEncoding);
PdfId font3Object = writer.newObject();
writer.startObj(font3Object);
PutDoc("<<\n/Type /Font\n/Subtype /Type3\n");
PutDoc("/Name /"+name+"S"+Pdf::toPdf(SubFonts)+"\n");
PutDoc("/FirstChar 0\n");
PutDoc("/LastChar "+Pdf::toPdf(glyphCount-1)+"\n");
PutDoc("/Widths "+Pdf::toPdf(fontWidths)+" 0 R\n");
PutDoc("/CharProcs "+Pdf::toPdf(fontCharProcs)+" 0 R\n");
PutDoc("/FontBBox ["+Pdf::toPdf(qRound(minx))+" "+Pdf::toPdf(qRound(miny))+" "+Pdf::toPdf(qRound(maxx))+ " "+Pdf::toPdf(qRound(maxy))+"]\n");
PutDoc("/FontMatrix [0.001 0 0 0.001 0 0]\n");
PutDoc("/Encoding "+Pdf::toPdf(fontEncoding)+" 0 R\n");
PutDoc(">>");
writer.endObj(font3Object);
pageData.FObjects[name+"S"+Pdf::toPdf(SubFonts)] = font3Object;
charProcs.clear();
glyphWidths.clear();
// glyphMapping.clear();
glyphCount = 0;
++SubFonts;
minx = std::numeric_limits<double>::max();
miny = std::numeric_limits<double>::max();
maxx = -std::numeric_limits<double>::max();
maxy = -std::numeric_limits<double>::max();
encoding = "<< /Type /Encoding\n/Differences [ 0\n";
}
}
result.glyphmap = glyphMapping;
return result;
}
 
 
PdfFont PDFLibCore::PDF_WriteGlyphsAsXForms(const QByteArray& fontName, ScFace& face, const QMap<uint,FPointArray>& RealGlyphs)
{
PdfFont result;
result.name = Pdf::toName(fontName);
result.method = Use_XForm;
result.encoding = Encode_224;
 
QByteArray fon;
QMap<uint,FPointArray>::ConstIterator ig;
for (ig = RealGlyphs.cbegin(); ig != RealGlyphs.cend(); ++ig)
{
FPoint np, np1, np2;
bool nPath = true;
fon.resize(0);
if (ig.value().size() > 3)
{
FPointArray gly = ig.value();
QTransform mat;
mat.scale(0.1, 0.1);
gly.map(mat);
for (int poi = 0; poi < gly.size()-3; poi += 4)
{
if (gly.isMarker(poi))
{
fon += "h\n";
nPath = true;
continue;
}
if (nPath)
{
np = gly.point(poi);
fon += FToStr(np.x())+" "+FToStr(-np.y())+" m\n";
nPath = false;
}
np = gly.point(poi+1);
np1 = gly.point(poi+3);
np2 = gly.point(poi+2);
fon += FToStr(np.x()) + " " + FToStr(-np.y()) + " " +
FToStr(np1.x()) + " " + FToStr(-np1.y()) + " " +
FToStr(np2.x()) + " " + FToStr(-np2.y()) + " c\n";
}
fon += "h f*\n";
np = getMinClipF(&gly);
np1 = getMaxClipF(&gly);
}
else
{
fon = "h";
np = FPoint(0, 0);
np1 = FPoint(0, 0);
}
PdfId fontGlyphXForm = writer.newObject();
writer.startObj(fontGlyphXForm);
PutDoc("<<\n/Type /XObject\n/Subtype /Form\n/FormType 1\n");
PutDoc("/BBox [ "+FToStr(np.x())+" "+FToStr(-np.y())+" "+FToStr(np1.x())+ " "+FToStr(-np1.y())+" ]\n");
PutDoc("/Resources << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]\n");
PutDoc(">>\n");
if (Options.Compress)
fon = CompressArray(fon);
PutDoc("/Length "+Pdf::toPdf(fon.length()+1));
if (Options.Compress)
PutDoc("\n/Filter /FlateDecode");
PutDoc(" >>\nstream\n"+EncStream(fon, fontGlyphXForm)+"\nendstream");
writer.endObj(fontGlyphXForm);
pageData.XObjects[fontName + Pdf::toPdf(ig.key())] = fontGlyphXForm;
}
return result;
}
 
PdfId PDFLibCore::PDF_EmbedFontObject(const QByteArray& font, const QByteArray& subtype)
 
{
PdfId embeddedFontObject = writer.newObject();
writer.startObj(embeddedFontObject);
int len = font.length();
QByteArray ttf = (Options.Compress? CompressArray(font) : font);
//qDebug() << QString("sfnt data: size=%1 compressed=%2").arg(len).arg(bb.length());
PutDoc("<<\n/Length " + Pdf::toPdf(ttf.length() + 1) + "\n");
PutDoc("/Length1 " + Pdf::toPdf(len) + "\n");
if (subtype.size() > 0)
PutDoc("/Subtype " + subtype);
if (Options.Compress)
PutDoc("/Filter /FlateDecode\n");
PutDoc(">>\nstream\n");
EncodeArrayToStream(ttf, embeddedFontObject);
PutDoc("\nendstream");
writer.endObj(embeddedFontObject);
return embeddedFontObject;
}
 
PdfId PDFLibCore::PDF_WriteFontDescriptor(const QByteArray& fontName, ScFace& face, ScFace::FontFormat fformat, PdfId embeddedFontObject)
{
PdfId fontDescriptor = writer.newObject();
writer.startObj(fontDescriptor);
// TODO: think about QByteArray ScFace::getFontDescriptor() -- AV
PutDoc("<<\n/Type /FontDescriptor\n");
PutDoc("/FontName " + Pdf::toName(fontName) + "\n");
PutDoc("/FontBBox [ " + Pdf::toAscii(face.pdfFontBBoxAsString()) + " ]\n");
PutDoc("/Flags ");
//FIXME: isItalic() should be queried from ScFace, not from Qt -- AV
//QFontInfo fo = QFontInfo(it.data());
int pfl = 0;
if (face.isFixedPitch())
pfl = pfl ^ 1;
//if (fo.italic())
if (face.italicAngleAsString() != "0")
pfl = pfl ^ 64;
// pfl = pfl ^ 4;
pfl = pfl ^ 32;
PutDoc(Pdf::toPdf(pfl)+"\n");
PutDoc("/Ascent " + Pdf::toAscii(face.pdfAscentAsString())+"\n");
PutDoc("/Descent " + Pdf::toAscii(face.pdfDescentAsString())+"\n");
PutDoc("/CapHeight " + Pdf::toAscii(face.pdfCapHeightAsString())+"\n");
PutDoc("/ItalicAngle " + Pdf::toAscii(face.italicAngleAsString())+"\n");
PutDoc("/StemV 1\n");
if (embeddedFontObject != 0)
{
if (fformat == ScFace::SFNT || fformat == ScFace::TTCF)
{
if (face.type() == ScFace::OTF)
{
PutDoc("/FontFile3 "+Pdf::toPdf(embeddedFontObject)+" 0 R\n");
}
else
{
PutDoc("/FontFile2 "+Pdf::toPdf(embeddedFontObject)+" 0 R\n");
}
}
else if (fformat == ScFace::PFB)
PutDoc("/FontFile "+Pdf::toPdf(embeddedFontObject)+" 0 R\n");
else if (fformat == ScFace::PFA)
PutDoc("/FontFile "+Pdf::toPdf(embeddedFontObject)+" 0 R\n");
}
PutDoc(">>");
writer.endObj(fontDescriptor);
return fontDescriptor;
}
 
PdfFont PDFLibCore::PDF_EncodeCidFont(const QByteArray& fontName, ScFace& face, const QByteArray& baseFont, PdfId fontDes, const ScFace::FaceEncoding& gl, const QMap<uint,uint> glyphmap )
{
PdfFont result;
result.name = Pdf::toName(fontName);
result.method = glyphmap.isEmpty()? Use_Embedded : Use_Subset;
result.encoding = glyphmap.isEmpty()? Encode_IdentityH : Encode_Subset;
result.glyphmap = glyphmap;
PdfId fontWidths2 = writer.newObject();
writer.startObj(fontWidths2);
QList<QByteArray> toUnicodeMaps;
QList<int> toUnicodeMapsCount;
QByteArray toUnicodeMap = "";
int toUnicodeMapCounter = 0;
PutDoc("[ ");
QList<uint> keys = gl.uniqueKeys();
QList<uint>::iterator git;
bool seenNotDef = false;
for (git = keys.begin(); git != keys.end(); ++git)
{
uint gid = result.encoding == Encode_Subset? glyphmap[*git] : *git;
if (gid > 0 || !seenNotDef)
{
seenNotDef |= (gid == 0);
PutDoc(Pdf::toPdf(gid)+" ["+Pdf::toPdf(static_cast<int>(face.glyphWidth(*git)* 1000))+"] " );
QString tmp, tmp2;
tmp.sprintf("%04X", gid);
tmp2.sprintf("%04X", gl.value(*git).first);
toUnicodeMap += "<" + Pdf::toAscii(tmp)+ "> <" + Pdf::toAscii(tmp2) + ">\n";
toUnicodeMapCounter++;
if (toUnicodeMapCounter == 100)
{
toUnicodeMaps.append(toUnicodeMap);
toUnicodeMapsCount.append(toUnicodeMapCounter);
toUnicodeMap = "";
toUnicodeMapCounter = 0;
}
}
}
PutDoc("]");
writer.endObj(fontWidths2);
if (toUnicodeMapCounter != 0)
{
toUnicodeMaps.append(toUnicodeMap);
toUnicodeMapsCount.append(toUnicodeMapCounter);
}
QByteArray toUnicodeMapStream = "";
toUnicodeMapStream += "/CIDInit /ProcSet findresource begin\n";
toUnicodeMapStream += "12 dict begin\n";
toUnicodeMapStream += "begincmap\n";
toUnicodeMapStream += "/CIDSystemInfo <<\n";
toUnicodeMapStream += "/Registry (Adobe)\n";
toUnicodeMapStream += "/Ordering (UCS)\n";
toUnicodeMapStream += "/Supplement 0\n";
toUnicodeMapStream += ">> def\n";
toUnicodeMapStream += "/CMapName /Adobe-Identity-UCS def\n";
toUnicodeMapStream += "/CMapType 2 def\n";
toUnicodeMapStream += "1 begincodespacerange\n";
toUnicodeMapStream += "<0000> <FFFF>\n";
toUnicodeMapStream += "endcodespacerange\n";
for (int uniC = 0; uniC < toUnicodeMaps.count(); uniC++)
{
toUnicodeMapStream += Pdf::toPdf(toUnicodeMapsCount[uniC]);
toUnicodeMapStream += " beginbfchar\n";
toUnicodeMapStream += toUnicodeMaps[uniC];
toUnicodeMapStream += "endbfchar\n";
}
toUnicodeMapStream += "endcmap\n";
toUnicodeMapStream += "CMapName currentdict /CMap defineresource pop\n";
toUnicodeMapStream += "end\n";
toUnicodeMapStream += "end\n";
PdfId fontToUnicode2 = WritePDFStream(toUnicodeMapStream);
PdfId fontObject2 = writer.newObject();
writer.startObj(fontObject2);
PutDoc("<<\n/Type /Font\n/Subtype /Type0\n");
PutDoc("/Name " + Pdf::toName(fontName) + "\n");
PutDoc("/BaseFont "+ baseFont +"\n");
PutDoc("/Encoding /Identity-H\n");
PutDoc("/ToUnicode "+Pdf::toPdf(fontToUnicode2)+" 0 R\n");
PutDoc("/DescendantFonts [");
PutDoc("<</Type /Font");
if (face.type() == ScFace::OTF)
PutDoc("/Subtype /CIDFontType0");
else
PutDoc("/Subtype /CIDFontType2");
PutDoc("/BaseFont " + baseFont);
PutDoc("/FontDescriptor " + Pdf::toPdf(fontDes)+ " 0 R");
PutDoc("/CIDSystemInfo <</Ordering(Identity)/Registry(Adobe)/Supplement 0>>");
PutDoc("/DW 1000");
PutDoc("/W "+Pdf::toPdf(fontWidths2)+" 0 R");
PutDoc("/CIDToGIDMap /Identity");
PutDoc(">>"); // close CIDFont dictionary
PutDoc("]\n"); // close DescendantFonts array
PutDoc(">>");
writer.endObj(fontObject2);
pageData.FObjects[fontName] = fontObject2;
return result;
}
 
 
PdfFont PDFLibCore::PDF_EncodeSimpleFont(const QByteArray& fontName, ScFace& face, const QByteArray& baseFont, const QByteArray& subtype, bool isEmbedded, PdfId fontDes, const ScFace::FaceEncoding& gl)
{
PdfFont result;
result.name = Pdf::toName(fontName);
result.method = isEmbedded? Use_Embedded : Use_System;
result.encoding = Encode_224;
int nglyphs = 0;
ScFace::FaceEncoding::ConstIterator gli;
for (gli = gl.cbegin(); gli != gl.cend(); ++gli)
{
if (gli.key() > static_cast<uint>(nglyphs))
nglyphs = gli.key();
}
++nglyphs;
// qDebug() << QString("pdflib: nglyphs %1 max %2").arg(nglyphs).arg(face.maxGlyph());
uint Fcc = nglyphs / 224;
if ((nglyphs % 224) != 0)
Fcc += 1;
for (uint Fc = 0; Fc < Fcc; ++Fc)
{
PdfId fontWidths2 = writer.newObject();
writer.startObj(fontWidths2);
int chCount = 32;
PutDoc("[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ");
for (int ww = 32; ww < 256; ++ww)
{
uint glyph = 224 * Fc + ww - 32;
if (gl.contains(glyph))
PutDoc(Pdf::toPdf(static_cast<int>(face.glyphWidth(glyph)* 1000))+" ");
else
PutDoc("0 ");
chCount++;
if (signed(glyph) == nglyphs-1)
break;
}
PutDoc("]");
writer.endObj(fontWidths2);
PdfId fontEncoding2 = writer.newObject();
writer.startObj(fontEncoding2);
QStringList toUnicodeMaps;
QList<int> toUnicodeMapsCount;
QString toUnicodeMap = "";
int toUnicodeMapCounter = 0;
PutDoc("<< /Type /Encoding\n");
PutDoc("/Differences [ \n");
int crc = 0;
bool startOfSeq = true;
for (int ww2 = 32; ww2 < 256; ++ww2)
{
uint glyph = 224 * Fc + ww2 - 32;
ScFace::FaceEncoding::ConstIterator glIt = gl.find(glyph);
if (glIt != gl.cend() && !glIt.value().second.isEmpty())
{
if (startOfSeq)
{
PutDoc(Pdf::toPdf(ww2)+" ");
startOfSeq = false;
}
PutDoc(Pdf::toName(glIt.value().second)+" ");
QString tmp, tmp2;
tmp.sprintf("%02X", ww2);
tmp2.sprintf("%04X", glIt.value().first);
toUnicodeMap += "<" + Pdf::toAscii(tmp) + "> <" + Pdf::toAscii(tmp2) + ">\n";
//QString("<%1> <%2>\n").arg(tmp).arg((tmp2));
toUnicodeMapCounter++;
if (toUnicodeMapCounter == 100)
{
toUnicodeMaps.append(toUnicodeMap);
toUnicodeMapsCount.append(toUnicodeMapCounter);
toUnicodeMap = "";
toUnicodeMapCounter = 0;
}
crc++;
}
else
{
startOfSeq = true;
}
if (signed(glyph) == nglyphs-1)
break;
if (crc > 8)
{
PutDoc("\n");
crc = 0;
}
}
if (toUnicodeMapCounter != 0)
{
toUnicodeMaps.append(toUnicodeMap);
toUnicodeMapsCount.append(toUnicodeMapCounter);
}
PutDoc("]\n");
PutDoc(">>");
writer.endObj(fontEncoding2);
QByteArray toUnicodeMapStream = "";
toUnicodeMapStream += "/CIDInit /ProcSet findresource begin\n";
toUnicodeMapStream += "12 dict begin\n";
toUnicodeMapStream += "begincmap\n";
toUnicodeMapStream += "/CIDSystemInfo <<\n";
toUnicodeMapStream += "/Registry (Adobe)\n";
toUnicodeMapStream += "/Ordering (UCS)\n";
toUnicodeMapStream += "/Supplement 0\n";
toUnicodeMapStream += ">> def\n";
toUnicodeMapStream += "/CMapName /Adobe-Identity-UCS def\n";
toUnicodeMapStream += "/CMapType 2 def\n";
toUnicodeMapStream += "1 begincodespacerange\n";
toUnicodeMapStream += "<0000> <FFFF>\n";
toUnicodeMapStream += "endcodespacerange\n";
for (int uniC = 0; uniC < toUnicodeMaps.count(); uniC++)
{
toUnicodeMapStream += Pdf::toPdf(toUnicodeMapsCount[uniC]);
toUnicodeMapStream += " beginbfchar\n";
toUnicodeMapStream += toUnicodeMaps[uniC];
toUnicodeMapStream += "endbfchar\n";
}
toUnicodeMapStream += "endcmap\n";
toUnicodeMapStream += "CMapName currentdict /CMap defineresource pop\n";
toUnicodeMapStream += "end\n";
toUnicodeMapStream += "end\n";
PdfId fontToUnicode2 = WritePDFStream(toUnicodeMapStream);
PdfId fontObject2 = writer.newObject();
writer.startObj(fontObject2);
PutDoc("<<\n/Type /Font\n/Subtype ");
PutDoc(subtype + "\n");
PutDoc("/Name "+Pdf::toName(fontName)+"S"+Pdf::toPdf(Fc)+"\n");
PutDoc("/BaseFont "+baseFont+"\n");
PutDoc("/FirstChar 0\n");
PutDoc("/LastChar "+Pdf::toPdf(chCount-1)+"\n");
PutDoc("/Widths "+Pdf::toPdf(fontWidths2)+" 0 R\n");
PutDoc("/Encoding "+Pdf::toPdf(fontEncoding2)+" 0 R\n");
PutDoc("/ToUnicode "+Pdf::toPdf(fontToUnicode2)+" 0 R\n");
PutDoc("/FontDescriptor "+Pdf::toPdf(fontDes)+" 0 R\n");
PutDoc(">>");
writer.endObj(fontObject2);
pageData.FObjects[fontName + "S"+Pdf::toPdf(Fc)] = fontObject2;
} // for(Fc)
PdfId fontWidthsForm = writer.newObject();
writer.startObj(fontWidthsForm);
PutDoc("[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ");
for (int ww = 32; ww < 256; ++ww)
{
uint glyph = face.char2CMap(QChar(ww));
if (gl.contains(glyph))
PutDoc(Pdf::toPdf(static_cast<int>(face.glyphWidth(glyph)* 1000))+" ");
else
PutDoc("0 ");
}
PutDoc("]");
writer.endObj(fontWidthsForm);
PdfId fontObjectForm = writer.newObject();
PdfFont formFont;
formFont.name = Pdf::toName(fontName) + "Form";
formFont.usage = Used_in_Forms;
writer.startObj(fontObjectForm);
PutDoc("<<\n/Type /Font\n/Subtype ");
PutDoc(subtype + "\n");
// if (fformat == ScFace::SFNT || fformat == ScFace::TTCF)
// {
// PutDoc("/TrueType\n");
PutDoc("/Name " + formFont.name+ "\n");
pageData.FObjects[formFont.name] = fontObjectForm;
UsedFontsF.insert(fontName, formFont);
/* }
else
{
PutDoc("/Type1\n");
PutDoc("/Name /"+face.psName().replace( QRegExp("[\\s\\/\\{\\[\\]\\}\\<\\>\\(\\)\\%]"), "_" )+"\n");
pageData.FObjects[face.psName().replace( QRegExp("[\\s\\/\\{\\[\\]\\}\\<\\>\\(\\)\\%]"), "_" )] = ObjCounter;
UsedFontsF.insert(it.key(), "/"+face.psName().replace( QRegExp("[\\s\\/\\{\\[\\]\\}\\<\\>\\(\\)\\%]"), "_" ));
} */
PutDoc("/BaseFont "+Pdf::toName(sanitizeFontName(face.psName()))+"\n");
PutDoc("/Encoding << \n");
PutDoc("/Differences [ \n");
PutDoc("24 /breve /caron /circumflex /dotaccent /hungarumlaut /ogonek /ring /tilde\n");
PutDoc("39 /quotesingle 96 /grave 128 /bullet /dagger /daggerdbl /ellipsis /emdash /endash /florin /fraction /guilsinglleft /guilsinglright\n");
PutDoc("/minus /perthousand /quotedblbase /quotedblleft /quotedblright /quoteleft /quoteright /quotesinglbase /trademark /fi /fl /Lslash /OE /Scaron\n");
PutDoc("/Ydieresis /Zcaron /dotlessi /lslash /oe /scaron /zcaron 164 /currency 166 /brokenbar 168 /dieresis /copyright /ordfeminine 172 /logicalnot\n");
PutDoc("/.notdef /registered /macron /degree /plusminus /twosuperior /threesuperior /acute /mu 183 /periodcentered /cedilla /onesuperior /ordmasculine\n");
PutDoc("188 /onequarter /onehalf /threequarters 192 /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla /Egrave /Eacute /Ecircumflex\n");
PutDoc("/Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash\n");
PutDoc("/Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla\n");
PutDoc("/egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis\n");
PutDoc("/divide /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis\n");
PutDoc("] >>\n");
PutDoc("/FirstChar 0\n");
PutDoc("/LastChar 255\n");
PutDoc("/Widths "+Pdf::toPdf(fontWidthsForm)+" 0 R\n");
PutDoc("/FontDescriptor "+Pdf::toPdf(fontDes)+" 0 R\n");
PutDoc(">>");
writer.endObj(fontObjectForm);
 
return result;
}
 
 
static void dumpFont(QString name, QByteArray data)
{
QFile file(name);
file.open(QIODevice::WriteOnly);
QDataStream out(&file);
out.writeRawData(data.data(), data.length());
}
 
static void dumpCFF(QString name, cff::CFF font)
{
QFile file(name);
file.open(QIODevice::WriteOnly);
QDataStream out(&file);
font.dump(out);
}
 
 
PdfFont PDFLibCore::PDF_WriteTtfSubsetFont(const QByteArray& fontName, ScFace& face, const QMap<uint,FPointArray>& RealGlyphs)
{
QByteArray font;
face.RawData(font);
dumpFont(face.psName() + ".ttf", font);
QList<ScFace::gid_type> glyphs = RealGlyphs.uniqueKeys();
glyphs.removeAll(0);
glyphs.prepend(0);
QByteArray subset = sfnt::subsetFace(font, glyphs);
dumpFont(face.psName()+"subs.ttf", subset);
PdfId embeddedFontObj = PDF_EmbedFontObject(subset, QByteArray());
PdfId fontDes = PDF_WriteFontDescriptor(fontName, face, face.format(), embeddedFontObj);
QByteArray baseFont = Pdf::toName(sanitizeFontName(face.psName()));
ScFace::FaceEncoding fullEncoding, subEncoding;
QMap<uint,uint> glyphmap;
face.glyphNames(fullEncoding);
for (int i = 0; i < glyphs.length(); ++i)
{
glyphmap[glyphs[i]] = i;
qDebug() << glyphs[i] << " --> " << i << QChar(fullEncoding[glyphs[i]].first);
}
PdfFont result = PDF_EncodeCidFont(fontName, face, baseFont, fontDes, fullEncoding, glyphmap);
return result;
}
 
 
PdfFont PDFLibCore::PDF_WriteCffSubsetFont(const QByteArray& fontName, ScFace& face, const QMap<uint,FPointArray>& RealGlyphs)
{
// QByteArray sfnt; //TEST
// face.RawData(sfnt);
// QByteArray cff = sfnt::getTable(sfnt, "CFF ");
// dumpFont(fontName, cff);
// cff::CFF cfffont(cff);
// cfffont.dump();
// QByteArray subsetfont = cff::subsetFace(cff, it.value().keys());
// dumpFont(it.key() + "subs.cff", subsetfont);
// cff::CFF subset(subsetfont);
// subset.dump();
// PDF_WriteFontDescriptor(fontName, face, fformat, 0);
// // END
QByteArray font;
face.RawData(font);
font = sfnt::getTable(font, "CFF ");
dumpFont(face.psName() + ".cff", font);
QList<ScFace::gid_type> glyphs = RealGlyphs.uniqueKeys();
glyphs.removeAll(0);
glyphs.prepend(0);
QByteArray subset = cff::subsetFace(font, glyphs);
dumpFont(face.psName()+"subs.cff", subset);
PdfId embeddedFontObj = PDF_EmbedFontObject(subset, "/CIDFontType0C");
PdfId fontDes = PDF_WriteFontDescriptor(fontName, face, face.format(), embeddedFontObj);
QByteArray baseFont = Pdf::toName(sanitizeFontName(face.psName()));
ScFace::FaceEncoding fullEncoding, subEncoding;
QMap<uint,uint> glyphmap;
face.glyphNames(fullEncoding);
for (int i = 0; i < glyphs.length(); ++i)
{
glyphmap[glyphs[i]] = i;
qDebug() << glyphs[i] << " --> " << i << QChar(fullEncoding[glyphs[i]].first);
}
PdfFont result = PDF_EncodeCidFont(fontName, face, baseFont, fontDes, fullEncoding, glyphmap);
return result;
}
 
 
PdfId PDFLibCore::PDF_EmbedType1AsciiFontObject(const QByteArray& fon)
{
QByteArray fon2;
PdfId embeddedFontObject = writer.newObject();
writer.startObj(embeddedFontObject);
int len1 = fon.indexOf("eexec")+5;
fon2 = fon.left(len1) + "\n";
int len2 = fon.indexOf("0000000000000000000000000");
if (len2 == -1)
len2 = fon.length()+1;
// QString tm;
// uint value;
// bool ok = true;
// int count = 0;
// for (int xx = len1; xx < len2-1; ++xx)
// {
// tm = fon.at(xx);
// if ((tm == QChar(13)) || (tm == QChar(10)))
// continue;
// xx++;
// count++;
// tm += fon.at(xx);
// value = tm.toUInt(&ok, 16);
// fon2 += char(value);
// }
QByteArray hexData = QByteArray::fromHex(fon.mid(len1, len2-len1));
fon2 += hexData;
fon2 += fon.mid(len2);
if (Options.Compress)
fon2 = CompressArray(fon2);
PutDoc("<<\n/Length "+Pdf::toPdf(fon2.length()+1)+"\n");
PutDoc("/Length1 "+Pdf::toPdf(len1+1)+"\n");
PutDoc("/Length2 "+Pdf::toPdf(hexData.length())+"\n");
if(static_cast<int>(fon.length()-len2) == -1)
PutDoc("/Length3 0\n");
else
PutDoc("/Length3 "+Pdf::toPdf(fon.length()-len2)+"\n");
if (Options.Compress)
PutDoc("/Filter /FlateDecode\n");
PutDoc(">>\nstream\n"+EncStream(fon2, embeddedFontObject)+"\nendstream");
writer.endObj(embeddedFontObject);
return embeddedFontObject;
}
 
 
PdfId PDFLibCore::PDF_EmbedType1BinaryFontObject(const QByteArray& bb)
{
PdfId embeddedFontObject = writer.newObject();
QByteArray fon;
writer.startObj(embeddedFontObject);
int posi;
for (posi = 6; posi < bb.size(); ++posi)
{
if ((bb[posi] == static_cast<char>(0x80)) && (static_cast<int>(bb[posi+1]) == 2))
break;
fon += bb[posi];
}
int len1 = fon.length();
int ulen;
ulen = bb[posi+2] & 0xff;
ulen |= (bb[posi+3] << 8) & 0xff00;
ulen |= (bb[posi+4] << 16) & 0xff0000;
ulen |= (bb[posi+5] << 24) & 0xff000000;
if (ulen > bb.size())
ulen = bb.size()-7;
posi += 6;
for (int j = 0; j < ulen; ++j)
fon += bb[posi++];
posi += 6;
int len2 = fon.length()-len1;
for (int j = posi; j < bb.size(); ++j)
{
if ((bb[j] == static_cast<char>(0x80)) && (static_cast<int>(bb[j+1]) == 3))
break;
if (bb[j] == '\r')
fon += "\n";
else
fon += bb[j];
}
int len3 = fon.length()-len2-len1;
if (Options.Compress)
fon = CompressArray(fon);
PutDoc("<<\n/Length "+Pdf::toPdf(fon.length()+1)+"\n");
PutDoc("/Length1 "+Pdf::toPdf(len1)+"\n");
PutDoc("/Length2 "+Pdf::toPdf(len2)+"\n");
PutDoc("/Length3 "+Pdf::toPdf(len3)+"\n");
if (Options.Compress)
PutDoc("/Filter /FlateDecode\n");
PutDoc(">>\nstream\n"+EncStream(fon,embeddedFontObject)+"\nendstream");
writer.endObj(embeddedFontObject);
return embeddedFontObject;
}
 
 
PdfId PDFLibCore::PDF_EmbedFontObject(const QString& name, ScFace& face)
{
PdfId embeddedFontObject = 0;
if (Options.EmbedList.contains(name))
{
ScFace::FontFormat fformat = face.format();
QByteArray bb;
face.RawData(bb);
if (fformat == ScFace::PFB)
{
embeddedFontObject = PDF_EmbedType1BinaryFontObject(bb);
}
if (fformat == ScFace::PFA)
{
embeddedFontObject = PDF_EmbedType1AsciiFontObject(bb);
}
if (fformat == ScFace::SFNT || fformat == ScFace::TTCF)
{
QByteArray subtype;
if (face.type() == ScFace::OTF)
{
subtype = "/CIDFontType0C";
bb = sfnt::getTable(bb, "CFF ");
dumpCFF(face.psName() + ".cff", cff::CFF(bb));
bb = cff::extractFace(bb, 0);
dumpCFF(face.psName() + "full.cff", cff::CFF(bb));
}
embeddedFontObject = PDF_EmbedFontObject(bb, subtype);
}
}
return embeddedFontObject;
}
 
 
void PDFLibCore::PDF_Begin_WriteUsedFonts(SCFonts &AllFonts, const QMap<QString, QMap<uint, FPointArray> >& ReallyUsed)
{
qDebug() << "embed list:" << QStringList(Options.EmbedList).join(", ");
qDebug() << "subset list:" << QStringList(Options.SubsetList).join(", ");
qDebug() << "outline list:" << QStringList(Options.OutlineList).join(", ");
QMap<QString,QMap<uint, FPointArray> >::ConstIterator it;
int a = 0;
for (it = ReallyUsed.cbegin(); it != ReallyUsed.cend(); ++it)
{
ScFace& face(AllFonts[it.key()]);
ScFace::FontFormat fformat = face.format();
PdfFont pdfFont;
QByteArray fontName = QByteArray("Fo") + Pdf::toPdf(a);
qDebug() << "pdf font" << it.key();
if (Options.OutlineList.contains(it.key()))
{
pdfFont = PDF_WriteGlyphsAsXForms(fontName, face, it.value());
}
else
{
if (Options.SubsetList.contains(it.key()))
{
if (fformat == ScFace::SFNT || fformat == ScFace::TTCF)
{
if (face.type() == ScFace::TTF)
{
pdfFont = PDF_WriteTtfSubsetFont(fontName, face, it.value());
}
else
{
pdfFont = PDF_WriteCffSubsetFont(fontName, face, it.value());
}
}
else
{
pdfFont = PDF_WriteType3Font(fontName, face, it.value());
}
}
else
{
PdfId embeddedFontObject = PDF_EmbedFontObject(it.key(), face);
PdfId fontDescriptor = PDF_WriteFontDescriptor(fontName, face, fformat, embeddedFontObject);
ScFace::FaceEncoding gl;
face.glyphNames(gl);
QByteArray baseFont = Pdf::toName(sanitizeFontName(face.psName()));
if ((face.isSymbolic() || !face.hasNames() || Options.Version == PDFOptions::PDFVersion_X4 || face.type() == ScFace::OTF) &&
(fformat == ScFace::SFNT || fformat == ScFace::TTCF))
{
pdfFont = PDF_EncodeCidFont(fontName, face, baseFont, fontDescriptor, gl, QMap<uint,uint>());
}
else
{
QByteArray subtype = (fformat == ScFace::SFNT || fformat == ScFace::TTCF) ? "/TrueType" : "/Type1";
pdfFont = PDF_EncodeSimpleFont(fontName, face, baseFont, subtype, embeddedFontObject != 0, fontDescriptor, gl);
}
}
pdfFont.usage = Used_in_Content;
QString meth;
switch (pdfFont.method)
{
case Use_System:
meth = "Systemfont (no embedding)"; break;
case Use_Embedded:
meth = "Embed"; break;
case Use_Subset:
meth = "Subset"; break;
case Use_Type3:
meth = "Subset as Type3 font"; break;
case Use_XForm:
meth = "Outline (PDF XForm)"; break;
default:
meth = "?"; break;
}
qDebug() << pdfFont.name << "uses method" << meth << "and encoding" << pdfFont.encoding;
UsedFontsP.insert(it.key(), pdfFont);
a++;
}
}
}
 
void PDFLibCore::PDF_Begin_Colors()
{
if (Options.UseLPI)
{
uint halftones = newObject();
StartObj(halftones);
uint halftones = writer.newObject();
writer.startObj(halftones);
PutDoc("<<\n/Type /Halftone\n/HalftoneType 5\n");
QMap<QString,LPIData>::const_iterator itlp;
for (itlp = Options.LPISettings.constBegin(); itlp != Options.LPISettings.constEnd(); ++itlp)
{
PutDoc("/"+itlp.key()+"\n<<\n/Type /Halftone\n/HalftoneType 1\n/Frequency ");
PutDoc(QString::number(itlp.value().Frequency)+"\n/Angle "+QString::number(itlp.value().Angle)+"\n/SpotFunction ");
QString func ("");
PutDoc(Pdf::toName(itlp.key()) + "\n<<\n/Type /Halftone\n/HalftoneType 1\n/Frequency ");
PutDoc(Pdf::toPdf(itlp.value().Frequency)+"\n/Angle "+Pdf::toPdf(itlp.value().Angle)+"\n/SpotFunction ");
QByteArray func ("");
switch (itlp.value().SpotFunc)
{
case 0:
1867,20 → 2032,21
func = "/SimpleDot";
break;
}
PutDoc(func+"\n>>\n");
PutDoc(func + "\n>>\n");
}
PutDoc("/Default\n<<\n/Type /Halftone\n/HalftoneType 1\n/Frequency 50\n/Angle 45\n/SpotFunction /Round\n>>\n");
PutDoc(">>\nendobj\n");
HTName = ResNam+QString::number(ResCount);
Transpar[HTName] = writeGState("/HT "+QString::number(halftones)+" 0 R\n");
PutDoc(">>");
writer.endObj(halftones);
HTName = ResNam+Pdf::toPdf(ResCount);
Transpar[HTName] = writeGState("/HT "+Pdf::toPdf(halftones)+" 0 R\n");
ResCount++;
}
if ((doc.HasCMS) && (Options.UseProfiles) && (Options.Version != PDFOptions::PDFVersion_X1a))
{
uint iccProfileObject = newObject();
StartObj(iccProfileObject);
PdfId iccProfileObject = writer.newObject();
writer.startObj(iccProfileObject);
QByteArray dataP;
struct ICCD dataD;
PdfICCD dataD;
loadRawBytes(ScCore->InputProfiles[Options.SolidProf], dataP);
PutDoc("<<\n");
if (Options.Compress)
1892,20 → 2058,21
dataP = compData;
}
}
PutDoc("/Length "+QString::number(dataP.size()+1)+"\n");
PutDoc("/N "+QString::number(Options.SComp)+"\n");
PutDoc("/Length "+Pdf::toPdf(dataP.size()+1)+"\n");
PutDoc("/N "+Pdf::toPdf(Options.SComp)+"\n");
PutDoc(">>\nstream\n");
EncodeArrayToStream(dataP, iccProfileObject);
PutDoc("\nendstream\nendobj\n");
uint iccColorspace = newObject();
StartObj(iccColorspace);
dataD.ResName = ResNam+QString::number(ResCount);
dataD.ICCArray = "[ /ICCBased "+QString::number(iccProfileObject)+" 0 R ]";
PutDoc("\nendstream");
writer.endObj(iccProfileObject);
PdfId iccColorspace = writer.newObject();
writer.startObj(iccColorspace);
dataD.ResName = ResNam+Pdf::toPdf(ResCount);
dataD.ICCArray = "[ /ICCBased "+Pdf::toPdf(iccProfileObject)+" 0 R ]";
dataD.ResNum = iccColorspace;
dataD.components = Options.SComp;
ICCProfiles[Options.SolidProf] = dataD;
PutDoc("[ /ICCBased "+QString::number(iccProfileObject)+" 0 R ]\n");
PutDoc("endobj\n");
PutDoc("[ /ICCBased "+Pdf::toPdf(iccProfileObject)+" 0 R ]");
writer.endObj(iccColorspace);
ResCount++;
}
if (((Options.isGrayscale == false) && (Options.UseRGB == false)) && (Options.UseSpotColors))
1918,29 → 2085,31
{
CMYKColor cmykValues;
int cc, cm, cy, ck;
struct SpotC spotD;
PdfSpotC spotD;
ScColorEngine::getCMYKValues(colorsToUse[itf.key()], &doc, cmykValues);
cmykValues.getValues(cc, cm, cy, ck);
QString colorDesc = "{\ndup "+FToStr(static_cast<double>(cc) / 255)+"\nmul exch dup ";
QByteArray colorDesc = "{\ndup "+FToStr(static_cast<double>(cc) / 255)+"\nmul exch dup ";
colorDesc += FToStr(static_cast<double>(cm) / 255)+"\nmul exch dup ";
colorDesc += FToStr(static_cast<double>(cy) / 255)+"\nmul exch ";
colorDesc += FToStr(static_cast<double>(ck) / 255)+" mul }";
uint separationFunction = newObject();
StartObj(separationFunction);
PdfId separationFunction = writer.newObject();
writer.startObj(separationFunction);
PutDoc("<<\n/FunctionType 4\n");
PutDoc("/Domain [0.0 1.0]\n");
PutDoc("/Range [0.0 1.0 0.0 1.0 0.0 1.0 0.0 1.0]\n");
PutDoc("/Length "+QString::number(colorDesc.length()+1)+"\n");
PutDoc(">>\nstream\n"+EncStream(colorDesc, separationFunction)+"\nendstream\nendobj\n");
uint separationColorspace= newObject();
StartObj(separationColorspace);
PutDoc("[ /Separation /");
PutDoc("/Length "+Pdf::toPdf(colorDesc.length()+1)+"\n");
PutDoc(">>\nstream\n"+EncStream(colorDesc, separationFunction)+"\nendstream");
writer.endObj(separationFunction);
PdfId separationColorspace= writer.newObject();
writer.startObj(separationColorspace);
PutDoc("[ /Separation ");
if (colorsToUse[itf.key()].isRegistrationColor())
PutDoc("All");
PutDoc("/All");
else
PutDoc(itf.key().simplified().replace("#", "#23").replace( QRegExp("[\\s\\/\\{\\[\\]\\}\\<\\>\\(\\)\\%]"), "#20" ));
PutDoc(" /DeviceCMYK "+QString::number(separationFunction)+" 0 R ]\nendobj\n");
spotD.ResName = spotNam+QString::number(spotCount);
PutDoc(Pdf::toName(itf.key().simplified().replace("#", "#23").replace( QRegExp("[\\s\\/\\{\\[\\]\\}\\<\\>\\(\\)\\%]"), "#20" )));
PutDoc(" /DeviceCMYK "+Pdf::toObjRef(separationFunction)+" ]");
writer.endObj(separationColorspace);
spotD.ResName = spotNam+Pdf::toPdf(spotCount);
spotD.ResNum = separationColorspace;
spotMap.insert(itf.key(), spotD);
spotCount++;
1949,9 → 2118,9
}
if ((Options.cropMarks) || (Options.bleedMarks) || (Options.registrationMarks) || (Options.colorMarks) || (Options.docInfoMarks))
{
struct SpotC spotD;
uint registrationColorspace = newObject();
StartObj(registrationColorspace);
PdfSpotC spotD;
PdfId registrationColorspace = writer.newObject();
writer.startObj(registrationColorspace);
PutDoc("[ /Separation /All /DeviceCMYK\n");
PutDoc("<<\n/FunctionType 2\n");
PutDoc("/Domain [0.0 1.0]\n");
1959,33 → 2128,35
PutDoc("/C0 [0 0 0 0] \n");
PutDoc("/C1 [1 1 1 1] \n");
PutDoc("/N 1\n");
PutDoc(">>\n]\nendobj\n");
spotD.ResName = spotNam+QString::number(spotCount);
PutDoc(">>\n]");
writer.endObj(registrationColorspace);
spotD.ResName = spotNam+Pdf::toPdf(spotCount);
spotD.ResNum = registrationColorspace;
spotMapReg.insert("Register", spotD);
spotCount++;
}
}
 
 
void PDFLibCore::PDF_Begin_Layers()
{
if ( ((Options.Version == PDFOptions::PDFVersion_15) || (Options.Version == PDFOptions::PDFVersion_X4)) && (Options.useLayers))
{
ScLayer ll;
struct OCGInfo ocg;
PdfOCGInfo ocg;
ll.isPrintable = false;
ll.ID = 0;
int Lnr = 0;
QString ocgNam("oc");
QByteArray ocgNam("oc");
uint docLayersCount=doc.Layers.count();
for (uint la = 0; la < docLayersCount; ++la)
{
uint optionalContent = newObject();
QString tmp("");
PdfId optionalContent = writer.newObject();
doc.Layers.levelToLayer(ll, Lnr);
ocg.Name = ocgNam+tmp.setNum(ll.ID);
ocg.Name = ocgNam + Pdf::toPdf(ll.ID);
ocg.ObjNum = optionalContent;
ocg.visible = ll.isViewable;
OCGEntries.insert(ll.Name, ocg);
StartObj(optionalContent);
writer.startObj(optionalContent);
PutDoc("<<\n");
PutDoc("/Type /OCG\n");
PutDoc("/Name ");
2003,16 → 2174,16
PutDoc("/OFF");
PutDoc(">>>>");
PutDoc("\n");
PutDoc(">>\nendobj\n");
PutDoc(">>");
writer.endObj(optionalContent);
Lnr++;
}
}
return true;
}
 
bool PDFLibCore::PDF_TemplatePage(const ScPage* pag, bool )
{
QString tmp, tmpOut;
QByteArray tmp, tmpOut;
ActPageP = pag;
PageItem* ite;
QList<PageItem*> PItems;
2025,8 → 2196,9
double bLeft, bRight, bBottom, bTop;
getBleeds(pag, bLeft, bRight, bBottom, bTop);
 
Seite.AObjects.clear();
for (int la = 0; la < doc.Layers.count(); ++la)
pageData.AObjects.clear();
 
for (int la = 0; la < doc.Layers.count(); ++la)
{
doc.Layers.levelToLayer(ll, Lnr);
PItems = doc.MasterItems;
2033,7 → 2205,7
if ((ll.isPrintable) || (((Options.Version == PDFOptions::PDFVersion_15) || (Options.Version == PDFOptions::PDFVersion_X4)) && (Options.useLayers)))
{
if (((Options.Version == PDFOptions::PDFVersion_15) || (Options.Version == PDFOptions::PDFVersion_X4)) && (Options.useLayers))
PutPage("/OC /"+OCGEntries[ll.Name].Name+" BDC\n");
PutPage("/OC /" + OCGEntries[ll.Name].Name + " BDC\n");
for (int a = 0; a < PItems.count(); ++a)
{
Content = "";
2058,12 → 2230,12
PutPage("q\n");
if ((ite->doOverprint) && (!Options.UseRGB))
{
QString ShName = ResNam+QString::number(ResCount);
QByteArray ShName = ResNam + Pdf::toPdf(ResCount);
ResCount++;
Transpar[ShName] = writeGState("/OP true\n"
"/op true\n"
"/OPM 1\n");
PutPage("/"+ShName+" gs\n");
PutPage(Pdf::toName(ShName) + " gs\n");
}
/* Bookmarks on Master Pages do not make any sense */
// if ((ite->isBookmark) && (Options.Bookmarks))
2088,12 → 2260,12
// #8758: Custom dotted lines don't export properly to pdf
// Null values have to be exported if line end != flat
if ((da != 0) || (ite->lineEnd() != Qt::FlatCap))
PutPage(QString::number(da)+" ");
PutPage(Pdf::toPdf(da)+" ");
}
PutPage("] "+QString::number(ite->DashOffset)+" d\n");
PutPage("] " + Pdf::toPdf(ite->DashOffset)+" d\n");
}
else
PutPage("["+getDashString(ite->PLineArt, ite->lineWidth())+"] 0 d\n");
PutPage("[" + Pdf::toAscii(getDashString(ite->PLineArt, ite->lineWidth())) + "] 0 d\n");
switch (ite->PLineEnd)
{
case Qt::FlatCap:
2562,7 → 2734,7
case PageItem::Symbol:
if (doc.docPatterns.contains(ite->pattern()))
{
QString tmpD = "";
QByteArray tmpD = "";
ScPattern pat = doc.docPatterns[ite->pattern()];
PutPage("q\n");
PutPage(SetPathAndClip(ite));
2580,7 → 2752,7
PageItem* embedded = pat.items.at(em);
tmpD += "q\n";
tmpD += "1 0 0 1 "+FToStr(embedded->gXpos)+" "+FToStr(ite->height() - embedded->gYpos)+" cm\n";
QString output;
QByteArray output;
if (!PDF_ProcessItem(output, embedded, pag, pag->pageNr(), true))
return "";
tmpD += output;
2596,7 → 2768,7
case PageItem::Group:
if (ite->groupItemList.count() > 0)
{
QString tmpD = "";
QByteArray tmpD;
PutPage("q\n");
if (ite->groupClipping())
PutPage(SetPathAndClip(ite));
2614,7 → 2786,7
PageItem* embedded = ite->groupItemList.at(em);
tmpD += "q\n";
tmpD += "1 0 0 1 "+FToStr(embedded->gXpos)+" "+FToStr(ite->height() - embedded->gYpos)+" cm\n";
QString output;
QByteArray output;
patternStackPos.push(QPointF(embedded->gXpos, ite->height() - embedded->gYpos));
inPattern++; // We are not really exporting a pattern, but that fix gradient export
if (!PDF_ProcessItem(output, embedded, pag, pag->pageNr(), true, true))
2637,8 → 2809,8
break;
}
PutPage("Q\n");
uint templateObject = newObject();
StartObj(templateObject);
PdfId templateObject = writer.newObject();
writer.startObj(templateObject);
PutDoc("<<\n/Type /XObject\n/Subtype /Form\n/FormType 1\n");
double bleedRight = 0.0;
double bleedLeft = 0.0;
2647,77 → 2819,32
double maxBoxY = ActPageP->height()+Options.bleeds.top()+Options.bleeds.bottom();
PutDoc("/BBox [ "+FToStr(-bleedLeft)+" "+FToStr(-Options.bleeds.bottom())+" "+FToStr(maxBoxX)+" "+FToStr(maxBoxY)+" ]\n");
// PutDoc("/BBox [ 0 0 "+FToStr(ActPageP->width())+" "+FToStr(ActPageP->height())+" ]\n");
PutDoc("/Resources << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]\n");
if ((Seite.ImgObjects.count() != 0) || (Seite.XObjects.count() != 0))
{
PutDoc("/XObject <<\n");
QMap<QString,int>::Iterator it;
for (it = Seite.ImgObjects.begin(); it != Seite.ImgObjects.end(); ++it)
PutDoc("/"+it.key()+" "+QString::number(it.value())+" 0 R\n");
QMap<QString,int>::Iterator iti;
for (iti = Seite.XObjects.begin(); iti != Seite.XObjects.end(); ++iti)
PutDoc("/"+iti.key()+" "+QString::number(iti.value())+" 0 R\n");
PutDoc(">>\n");
}
if (Seite.FObjects.count() != 0)
{
PutDoc("/Font << \n");
QMap<QString,int>::Iterator it2;
for (it2 = Seite.FObjects.begin(); it2 != Seite.FObjects.end(); ++it2)
PutDoc("/"+it2.key()+" "+QString::number(it2.value())+" 0 R\n");
PutDoc(">>\n");
}
if (Shadings.count() != 0)
{
PutDoc("/Shading << \n");
QMap<QString,int>::Iterator it3;
for (it3 = Shadings.begin(); it3 != Shadings.end(); ++it3)
PutDoc("/"+it3.key()+" "+QString::number(it3.value())+" 0 R\n");
PutDoc(">>\n");
}
if (Patterns.count() != 0)
{
PutDoc("/Pattern << \n");
QMap<QString,int>::Iterator it3p;
for (it3p = Patterns.begin(); it3p != Patterns.end(); ++it3p)
PutDoc("/"+it3p.key()+" "+QString::number(it3p.value())+" 0 R\n");
PutDoc(">>\n");
}
if (Transpar.count() != 0)
{
PutDoc("/ExtGState << \n");
QMap<QString,int>::Iterator it3t;
for (it3t = Transpar.begin(); it3t != Transpar.end(); ++it3t)
PutDoc("/"+it3t.key()+" "+QString::number(it3t.value())+" 0 R\n");
PutDoc(">>\n");
}
if ((ICCProfiles.count() != 0) || (spotMap.count() != 0))
{
PutDoc("/ColorSpace << \n");
QMap<QString,ICCD>::Iterator it3c;
if (ICCProfiles.count() != 0)
{
for (it3c = ICCProfiles.begin(); it3c != ICCProfiles.end(); ++it3c)
PutDoc("/"+it3c.value().ResName+" "+QString::number(it3c.value().ResNum)+" 0 R\n");
}
QMap<QString,SpotC>::Iterator it3sc;
if (spotMap.count() != 0)
{
for (it3sc = spotMap.begin(); it3sc != spotMap.end(); ++it3sc)
PutDoc("/"+it3sc.value().ResName+" "+QString::number(it3sc.value().ResNum)+" 0 R\n");
}
PutDoc(">>\n");
}
PutDoc(">>\n");
Pdf::ResourceDictionary dict;
dict.XObject.unite(pageData.ImgObjects);
dict.XObject.unite(pageData.XObjects);
dict.Font = pageData.FObjects;
dict.Shading = Shadings;
dict.Pattern = Patterns;
dict.ExtGState = Transpar;
dict.ColorSpace.append(asColorSpace(ICCProfiles.values()));
dict.ColorSpace.append(asColorSpace(spotMap.values()));
writer.write("/Resources ");
writer.write(dict);
if (Options.Compress)
Content = CompressStr(&Content);
PutDoc("/Length "+QString::number(Content.length()+1));
Content = CompressArray(Content);
PutDoc("/Length "+Pdf::toPdf(Content.length()+1));
if (Options.Compress)
PutDoc("\n/Filter /FlateDecode");
PutDoc(" >>\nstream\n"+EncStream(Content, templateObject)+"\nendstream\nendobj\n");
int pIndex = doc.MasterPages.indexOf((ScPage* const) pag) + 1;
QString name = QString("master_page_obj_%1_%2").arg(pIndex).arg(qHash(ite));
Seite.XObjects[name] = templateObject;
PutDoc(" >>\nstream\n"+EncStream(Content, templateObject)+"\nendstream");
writer.endObj(templateObject);
int pIndex = doc.MasterPages.indexOf((ScPage* const) pag) + 1;
QByteArray name = QByteArray("master_page_obj_%1_%2")
.replace("%1", Pdf::toPdf(pIndex))
.replace("%2", Pdf::toPdf(qHash(ite)));
pageData.XObjects[name] = templateObject;
}
if (((Options.Version == PDFOptions::PDFVersion_15) || (Options.Version == PDFOptions::PDFVersion_X4)) && (Options.useLayers))
PutPage("EMC\n");
2727,12 → 2854,14
return true;
}
 
 
 
void PDFLibCore::PDF_Begin_Page(const ScPage* pag, QPixmap pm)
{
ActPageP = pag;
Content = "";
Seite.AObjects.clear();
Seite.radioButtonList.clear();
pageData.AObjects.clear();
pageData.radioButtonList.clear();
if (Options.Thumbnails)
{
ScImage img(pm.toImage());
2747,25 → 2876,26
compDataAvail = true;
}
}
uint thumbnail = newObject();
StartObj(thumbnail);
PutDoc("<<\n/Width "+QString::number(img.width())+"\n");
PutDoc("/Height "+QString::number(img.height())+"\n");
uint thumbnail = writer.newObject();
writer.startObj(thumbnail);
PutDoc("<<\n/Width "+Pdf::toPdf(img.width())+"\n");
PutDoc("/Height "+Pdf::toPdf(img.height())+"\n");
PutDoc("/ColorSpace /DeviceRGB\n/BitsPerComponent 8\n");
 
PutDoc("/Length "+QString::number(array.size()+1)+"\n");
PutDoc("/Length "+Pdf::toPdf(array.size()+1)+"\n");
if (Options.Compress && compDataAvail)
PutDoc("/Filter /FlateDecode\n");
PutDoc(">>\nstream\n");
EncodeArrayToStream(array, thumbnail);
PutDoc("\nendstream\nendobj\n");
Seite.Thumb = thumbnail;
PutDoc("\nendstream");
writer.endObj(thumbnail);
pageData.Thumb = thumbnail;
}
}
 
void PDFLibCore::PDF_End_Page(int physPage)
{
if (!Seite.radioButtonList.isEmpty())
if (!pageData.radioButtonList.isEmpty())
PDF_RadioButtons();
uint PgNr = ActPageP->pageNr();
double markOffs = 0.0;
2852,7 → 2982,7
if (Options.registrationMarks)
{
double regDelta = markOffs - Options.markOffset;
QString regCross = "0 7 m\n14 7 l\nh\n7 0 m\n7 14 l\nh\n13 7 m\n13 10.31383 10.31383 13 7 13 c\n3.68629 13 1 10.31383 1 7 c\n1 3.68629 3.68629 1 7 1 c\n";
QByteArray regCross = "0 7 m\n14 7 l\nh\n7 0 m\n7 14 l\nh\n13 7 m\n13 10.31383 10.31383 13 7 13 c\n3.68629 13 1 10.31383 1 7 c\n1 3.68629 3.68629 1 7 1 c\n";
regCross += "10.31383 1 13 3.68629 13 7 c\nh\n10.5 7 m\n10.5 8.93307 8.93307 10.5 7 10.5 c\n5.067 10.5 3.5 8.93307 3.5 7 c\n";
regCross += "3.5 5.067 5.067 3.5 7 3.5 c\n8.93307 3.5 10.5 5.067 10.5 7 c\nh\nS\n";
PutPage("q\n");
2937,7 → 3067,7
docTitle = fi.fileName();
}
// docTitle += " "+ tr("Page:")+" "+tmp.setNum(PgNr+1);
docTitle += " "+ tr("Page:")+" "+ QString::number(PgNr+1);
docTitle += " "+ tr("Page:")+" "+ Pdf::toPdf(PgNr+1);
PutPage("/"+spotMapReg["Register"].ResName+" cs 1 scn\n");
PutPage("q\n");
PutPage("1 0 0 1 "+FToStr(startX)+" "+FToStr(startY)+" cm\n");
2957,12 → 3087,12
PutPage("Q\n");
}
}
Seite.ObjNum = WritePDFStream(Content);
pageData.ObjNum = WritePDFStream(Content);
int Gobj = 0;
if ((Options.Version >= PDFOptions::PDFVersion_14) || (Options.Version == PDFOptions::PDFVersion_X4))
{
Gobj = newObject();
StartObj(Gobj);
Gobj = writer.newObject();
writer.startObj(Gobj);
PutDoc("<< /S /Transparency\n");
if (Options.UseRGB)
PutDoc("/CS /DeviceRGB\n");
2973,15 → 3103,16
else
{
if ((doc.HasCMS) && (Options.UseProfiles))
PutDoc("/CS "+ICCProfiles[Options.SolidProf].ICCArray+"\n");
PutDoc("/CS " + ICCProfiles[Options.SolidProf].ICCArray + "\n");
else
PutDoc("/CS /DeviceCMYK\n");
}
}
PutDoc(">>\nendobj\n");
PutDoc(">>");
writer.endObj(Gobj);
}
uint pageObject = newObject();
StartObj(pageObject);
uint pageObject = writer.newObject();
writer.startObj(pageObject);
PutDoc("<<\n/Type /Page\n/Parent 4 0 R\n");
PutDoc("/MediaBox [0 0 "+FToStr(maxBoxX)+" "+FToStr(maxBoxY)+"]\n");
PutDoc("/BleedBox ["+FToStr(markOffs)+" "+FToStr(markOffs)+" "+FToStr(maxBoxX-markOffs)+" "+FToStr(maxBoxY-markOffs)+"]\n");
2989,27 → 3120,27
PutDoc("/TrimBox ["+FToStr(bleedLeft+markOffs)+" "+FToStr(Options.bleeds.bottom()+markOffs)+" "+FToStr(maxBoxX-bleedRight-markOffs)+" "+FToStr(maxBoxY-Options.bleeds.top()-markOffs)+"]\n");
if (Options.Version >= PDFOptions::PDFVersion_13) // PDF/X forbids having both art and trim box!
PutDoc("/ArtBox ["+FToStr(bleedLeft+markOffs)+" "+FToStr(Options.bleeds.bottom()+markOffs)+" "+FToStr(maxBoxX-bleedRight-markOffs)+" "+FToStr(maxBoxY-Options.bleeds.top()-markOffs)+"]\n");
PutDoc("/Rotate "+QString::number(Options.RotateDeg)+"\n");
PutDoc("/Contents "+QString::number(Seite.ObjNum)+" 0 R\n");
PutDoc("/Rotate "+Pdf::toPdf(Options.RotateDeg)+"\n");
PutDoc("/Contents "+Pdf::toPdf(pageData.ObjNum)+" 0 R\n");
if ((Options.Version >= PDFOptions::PDFVersion_14) || (Options.Version == PDFOptions::PDFVersion_X4)) // && (Transpar.count() != 0))
PutDoc("/Group "+QString::number(Gobj)+" 0 R\n");
PutDoc("/Group "+Pdf::toPdf(Gobj)+" 0 R\n");
if (Options.Thumbnails)
PutDoc("/Thumb "+QString::number(Seite.Thumb)+" 0 R\n");
if (Seite.AObjects.count() != 0)
PutDoc("/Thumb "+Pdf::toPdf(pageData.Thumb)+" 0 R\n");
if (pageData.AObjects.count() != 0)
{
PutDoc("/Annots [ ");
for (int b = 0; b < Seite.AObjects.count(); ++b)
PutDoc(QString::number(Seite.AObjects[b])+" 0 R ");
for (int b = 0; b < pageData.AObjects.count(); ++b)
PutDoc(Pdf::toPdf(pageData.AObjects[b])+" 0 R ");
PutDoc("]\n");
}
if (Options.PresentMode)
{
if (ActPageP->PresentVals.pageViewDuration > 0)
PutDoc("/Dur "+QString::number(ActPageP->PresentVals.pageViewDuration)+"\n");
PutDoc("/Dur "+Pdf::toPdf(ActPageP->PresentVals.pageViewDuration)+"\n");
if (ActPageP->PresentVals.effectType != 0)
{
PutDoc("/Trans << /Type /Trans\n");
PutDoc("/D "+QString::number(ActPageP->PresentVals.pageEffectDuration)+"\n");
PutDoc("/D "+Pdf::toPdf(ActPageP->PresentVals.pageEffectDuration)+"\n");
switch (ActPageP->PresentVals.effectType)
{
case 1:
3147,33 → 3278,36
PutDoc(">>\n");
}
}
PutDoc(">>\nendobj\n");
PutDoc(">>");
writer.endObj(pageObject);
PageTree.Count++;
PageTree.Kids[physPage] = pageObject;
}
 
 
void PDFLibCore::writeXObject(uint objNr, QString dictionary, QByteArray stream)
void PDFLibCore::writeXObject(uint objNr, QByteArray dictionary, QByteArray stream)
{
StartObj(objNr);
writer.startObj(objNr);
PutDoc("<<");
PutDoc(dictionary);
PutDoc(">>\nstream\n");
EncodeArrayToStream(stream, objNr);
PutDoc("\nendstream\nendobj\n");
PutDoc("\nendstream");
writer.endObj(objNr);
}
 
 
uint PDFLibCore::writeObject(QString type, QString dictionary)
PdfId PDFLibCore::writeObject(QByteArray type, QByteArray dictionary)
{
uint result = newObject();
StartObj(result);
PdfId result = writer.newObject();
writer.startObj(result);
PutDoc("<<");
if (!type.isEmpty())
PutDoc("/Type " + type + "\n");
PutDoc(dictionary);
PutDoc(">>\nendobj\n");
return result;
PutDoc(">>");
writer.endObj(result);
return result;
}
 
 
3183,7 → 3317,7
ScLayer ll;
ll.isPrintable = false;
if (Options.UseLPI)
PutPage("/"+HTName+" gs\n");
PutPage(Pdf::toName(HTName) + " gs\n");
double bleedRight = 0.0;
double bleedLeft = 0.0;
double bleedBottom = 0.0;
3209,7 → 3343,9
{
double bbWidth = ActPageP->width() + bleedLeft + bleedRight;
double bbHeight = ActPageP->height() + bleedBottom + bleedTop;
PutPage( QString("%1 %2 %3 %4 re W n\n").arg(FToStr(-bleedLeft)).arg(FToStr(-bleedBottom)).arg(FToStr(bbWidth)).arg(FToStr(bbHeight)) );
const char* B = " ";
PutPage(FToStr(-bleedLeft) +B+ FToStr(-bleedBottom) +B+
FToStr(bbWidth) +B+ FToStr(bbHeight) + " re W n\n");
}
if ( (Options.MirrorH) && (!pag->MPageNam.isEmpty()) )
PutPage("-1 0 0 1 "+FToStr(ActPageP->width())+" 0 cm\n");
3241,7 → 3377,7
bool PDFLibCore::PDF_ProcessMasterElements(const ScLayer& layer, const ScPage* pag, uint PNr)
{
PageItem* ite;
QString content, output;
QByteArray content, output;
QList<PageItem*> PItems;
 
if (pag->MPageNam.isEmpty())
3266,7 → 3402,9
continue;
if ((!pag->pageName().isEmpty()) && (ite->OwnPage != static_cast<int>(pag->pageNr())) && (ite->OwnPage != -1))
continue;
QString name = QString("/master_page_obj_%1_%2").arg(mPageIndex).arg(qHash(ite));
QByteArray name = QByteArray("/master_page_obj_%1_%2")
.replace("%1", Pdf::toPdf(mPageIndex))
.replace("%2", Pdf::toPdf(qHash(ite)));
if ((!ite->asTextFrame()) && (!ite->asPathText()) && (!ite->asTable()))
{
if (((layer.transparency != 1) || (layer.blendMode != 0)) && ((Options.Version >= PDFOptions::PDFVersion_14) || (Options.Version == PDFOptions::PDFVersion_X4)))
3296,21 → 3434,22
// Couldn't we use Write_TransparencyGroup() here?
if (((layer.transparency != 1) || (layer.blendMode != 0)) && ((Options.Version >= PDFOptions::PDFVersion_14) ||(Options.Version == PDFOptions::PDFVersion_X4)))
{
int Gobj = newObject();
StartObj(Gobj);
PdfId Gobj = writer.newObject();
writer.startObj(Gobj);
PutDoc("<< /Type /Group\n");
PutDoc("/S /Transparency\n");
PutDoc("/I false\n");
PutDoc("/K false\n");
PutDoc(">>\nendobj\n");
QString ShName = ResNam+QString::number(ResCount);
PutDoc(">>");
writer.endObj(Gobj);
QByteArray ShName = ResNam+QByteArray::number(ResCount);
ResCount++;
Transpar[ShName] = writeGState("/CA "+FToStr(layer.transparency)+"\n"
+ "/ca "+FToStr(layer.transparency)+"\n"
+ "/SMask /None\n/AIS false\n/OPM 1\n"
+ "/BM /" + blendMode(layer.blendMode) + "\n");
uint formObject = newObject();
StartObj(formObject);
PdfId formObject = writer.newObject();
writer.startObj(formObject);
PutDoc("<<\n/Type /XObject\n/Subtype /Form\n/FormType 1\n");
double bleedRight = 0.0;
double bleedLeft = 0.0;
3318,16 → 3457,17
double maxBoxX = ActPageP->width()+bleedRight+bleedLeft;
double maxBoxY = ActPageP->height()+Options.bleeds.top()+Options.bleeds.bottom();
PutDoc("/BBox [ "+FToStr(-bleedLeft)+" "+FToStr(-Options.bleeds.bottom())+" "+FToStr(maxBoxX)+" "+FToStr(maxBoxY)+" ]\n");
PutDoc("/Group "+QString::number(Gobj)+" 0 R\n");
PutDoc("/Group "+QByteArray::number(Gobj)+" 0 R\n");
if (Options.Compress)
content = CompressStr(&content);
PutDoc("/Length "+QString::number(content.length()+1));
content = CompressArray(content);
PutDoc("/Length "+QByteArray::number(content.length()+1));
if (Options.Compress)
PutDoc("\n/Filter /FlateDecode");
PutDoc(" >>\nstream\n"+EncStream(content, formObject)+"\nendstream\nendobj\n");
QString name = ResNam+QString::number(ResCount);
PutDoc(" >>\nstream\n"+EncStream(content, formObject)+"\nendstream");
writer.endObj(formObject);
QByteArray name = ResNam+QByteArray::number(ResCount);
ResCount++;
Seite.XObjects[name] = formObject;
pageData.XObjects[name] = formObject;
PutPage("q\n");
PutPage("/"+ShName+" gs\n");
PutPage("/"+name+" Do\n");
3342,7 → 3482,7
bool PDFLibCore::PDF_ProcessPageElements(const ScLayer& layer, const ScPage* pag, uint PNr)
{
PageItem* ite;
QString output;
QByteArray output;
QList<PageItem*> PItems;
 
int pc_exportpagesitems = usingGUI ? progressDialog->progress("ECPI") : 0;
3349,7 → 3489,7
PItems = (pag->pageName().isEmpty()) ? doc.DocItems : doc.MasterItems;
if ((layer.isPrintable) || (((Options.Version == PDFOptions::PDFVersion_15) || (Options.Version == PDFOptions::PDFVersion_X4)) && (Options.useLayers)))
{
QString inh = "";
QByteArray inh = "";
if (((Options.Version == PDFOptions::PDFVersion_15) || (Options.Version == PDFOptions::PDFVersion_X4)) && (Options.useLayers))
PutPage("/OC /"+OCGEntries[layer.Name].Name+" BDC\n");
for (int a = 0; a < PItems.count() && !abortExport; ++a)
3372,21 → 3512,22
// Couldn't we use Write_TransparencyGroup() here?
if (((layer.transparency != 1) || (layer.blendMode != 0)) && ((Options.Version >= PDFOptions::PDFVersion_14) ||(Options.Version == PDFOptions::PDFVersion_X4)))
{
int Gobj = newObject();
StartObj(Gobj);
int Gobj = writer.newObject();
writer.startObj(Gobj);
PutDoc("<< /Type /Group\n");
PutDoc("/S /Transparency\n");
PutDoc("/I false\n");
PutDoc("/K false\n");
PutDoc(">>\nendobj\n");
QString ShName = ResNam+QString::number(ResCount);
PutDoc(">>");
writer.endObj(Gobj);
QByteArray ShName = ResNam+Pdf::toPdf(ResCount);
ResCount++;
Transpar[ShName] = writeGState("/CA "+FToStr(layer.transparency)+"\n"
+ "/ca "+FToStr(layer.transparency)+"\n"
+ "/SMask /None\n/AIS false\n/OPM 1\n"
+ "/BM /" + blendMode(layer.blendMode) + "\n");
uint formObject = newObject();
StartObj(formObject);
PdfId formObject = writer.newObject();
writer.startObj(formObject);
PutDoc("<<\n/Type /XObject\n/Subtype /Form\n/FormType 1\n");
double bleedRight = 0.0;
double bleedLeft = 0.0;
3394,18 → 3535,19
double maxBoxX = ActPageP->width()+bleedRight+bleedLeft;
double maxBoxY = ActPageP->height()+Options.bleeds.top()+Options.bleeds.bottom();
PutDoc("/BBox [ "+FToStr(-bleedLeft)+" "+FToStr(-Options.bleeds.bottom())+" "+FToStr(maxBoxX)+" "+FToStr(maxBoxY)+" ]\n");
PutDoc("/Group "+QString::number(Gobj)+" 0 R\n");
PutDoc("/Group "+Pdf::toPdf(Gobj)+" 0 R\n");
if (Options.Compress)
inh = CompressStr(&inh);
PutDoc("/Length "+QString::number(inh.length()+1));
inh = CompressArray(inh);
PutDoc("/Length "+Pdf::toPdf(inh.length()+1));
if (Options.Compress)
PutDoc("\n/Filter /FlateDecode");
PutDoc(" >>\nstream\n"+EncStream(inh, formObject)+"\nendstream\nendobj\n");
QString name = layer.Name.simplified().replace(QRegExp("[\\s\\/\\{\\[\\]\\}\\<\\>\\(\\)\\%]"), "_") + QString::number(layer.ID) + QString::number(PNr);
Seite.XObjects[name] = formObject;
PutDoc(" >>\nstream\n"+EncStream(inh, formObject)+"\nendstream");
writer.endObj(formObject);
QByteArray name = Pdf::toPdfDocEncoding(layer.Name.simplified().replace(QRegExp("[\\s\\/\\{\\[\\]\\}\\<\\>\\(\\)\\%]"), "_")) + Pdf::toPdf(layer.ID) + Pdf::toPdf(PNr);
pageData.XObjects[name] = formObject;
PutPage("q\n");
PutPage("/"+ShName+" gs\n");
PutPage("/"+name+" Do\n");
PutPage(Pdf::toName(ShName) + " gs\n");
PutPage(Pdf::toName(name) + " Do\n");
PutPage("Q\n");
}
if (((Options.Version == PDFOptions::PDFVersion_15) || (Options.Version == PDFOptions::PDFVersion_X4)) && (Options.useLayers))
3414,11 → 3556,11
return true;
}
 
QString PDFLibCore::Write_FormXObject(QString &data, PageItem *controlItem)
QByteArray PDFLibCore::Write_FormXObject(QByteArray &data, PageItem *controlItem)
{
QString retString = "";
uint formObject = newObject();
StartObj(formObject);
QByteArray retString = "";
PdfId formObject = writer.newObject();
writer.startObj(formObject);
PutDoc("<<\n/Type /XObject\n/Subtype /Form\n/FormType 1\n");
double bleedRight = 0.0;
double bleedLeft = 0.0;
3457,77 → 3599,29
}
else
PutDoc("/BBox [ "+FToStr(-bleedLeft)+" "+FToStr(-Options.bleeds.bottom())+" "+FToStr(maxBoxX)+" "+FToStr(maxBoxY)+" ]\n");
PutDoc("/Resources << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]\n");
if ((Seite.ImgObjects.count() != 0) || (Seite.XObjects.count() != 0))
{
PutDoc("/XObject <<\n");
QMap<QString,int>::Iterator it;
for (it = Seite.ImgObjects.begin(); it != Seite.ImgObjects.end(); ++it)
PutDoc("/"+it.key()+" "+QString::number(it.value())+" 0 R\n");
QMap<QString,int>::Iterator iti;
for (iti = Seite.XObjects.begin(); iti != Seite.XObjects.end(); ++iti)
PutDoc("/"+iti.key()+" "+QString::number(iti.value())+" 0 R\n");
PutDoc(">>\n");
}
if (Seite.FObjects.count() != 0)
{
PutDoc("/Font << \n");
QMap<QString,int>::Iterator it2;
for (it2 = Seite.FObjects.begin(); it2 != Seite.FObjects.end(); ++it2)
PutDoc("/"+it2.key()+" "+QString::number(it2.value())+" 0 R\n");
PutDoc(">>\n");
}
if (Shadings.count() != 0)
{
PutDoc("/Shading << \n");
QMap<QString,int>::Iterator it3;
for (it3 = Shadings.begin(); it3 != Shadings.end(); ++it3)
PutDoc("/"+it3.key()+" "+QString::number(it3.value())+" 0 R\n");
PutDoc(">>\n");
}
if (Patterns.count() != 0)
{
PutDoc("/Pattern << \n");
QMap<QString,int>::Iterator it3p;
for (it3p = Patterns.begin(); it3p != Patterns.end(); ++it3p)
PutDoc("/"+it3p.key()+" "+QString::number(it3p.value())+" 0 R\n");
PutDoc(">>\n");
}
if (Transpar.count() != 0)
{
PutDoc("/ExtGState << \n");
QMap<QString,int>::Iterator it3t;
for (it3t = Transpar.begin(); it3t != Transpar.end(); ++it3t)
PutDoc("/"+it3t.key()+" "+QString::number(it3t.value())+" 0 R\n");
PutDoc(">>\n");
}
if ((ICCProfiles.count() != 0) || (spotMap.count() != 0))
{
PutDoc("/ColorSpace << \n");
QMap<QString,ICCD>::Iterator it3c;
if (ICCProfiles.count() != 0)
{
for (it3c = ICCProfiles.begin(); it3c != ICCProfiles.end(); ++it3c)
PutDoc("/"+it3c.value().ResName+" "+QString::number(it3c.value().ResNum)+" 0 R\n");
}
QMap<QString,SpotC>::Iterator it3sc;
if (spotMap.count() != 0)
{
for (it3sc = spotMap.begin(); it3sc != spotMap.end(); ++it3sc)
PutDoc("/"+it3sc.value().ResName+" "+QString::number(it3sc.value().ResNum)+" 0 R\n");
}
PutDoc(">>\n");
}
PutDoc("/Resources ");
Pdf::ResourceDictionary dict;
dict.XObject.unite(pageData.ImgObjects);
dict.XObject.unite(pageData.XObjects);
dict.Font = pageData.FObjects;
dict.Shading = Shadings;
dict.Pattern = Patterns;
dict.ExtGState = Transpar;
dict.ColorSpace.append(asColorSpace(ICCProfiles.values()));
dict.ColorSpace.append(asColorSpace(spotMap.values()));
writer.write(dict);
 
PutDoc(">>\n");
if (Options.Compress)
data = CompressStr(&data);
PutDoc("/Length "+QString::number(data.length()+1));
data = CompressArray(data);
PutDoc("/Length "+QByteArray::number(data.length()+1));
if (Options.Compress)
PutDoc("\n/Filter /FlateDecode");
PutDoc(" >>\nstream\n"+EncStream(data, formObject)+"\nendstream\nendobj\n");
QString name = ResNam+QString::number(ResCount);
PutDoc(" >>\nstream\n"+EncStream(data, formObject)+"\nendstream");
writer.endObj(formObject);
QByteArray name = ResNam+QByteArray::number(ResCount);
ResCount++;
Seite.XObjects[name] = formObject;
pageData.XObjects[name] = formObject;
retString += "q\n";
retString += "/"+name+" Do\n";
retString += "Q\n";
3534,17 → 3628,18
return retString;
}
 
QString PDFLibCore::Write_TransparencyGroup(double trans, int blend, QString &data, PageItem *controlItem)
QByteArray PDFLibCore::Write_TransparencyGroup(double trans, int blend, QByteArray &data, PageItem *controlItem)
{
QString ShName = "";
QString retString = "";
int Gobj = newObject();
StartObj(Gobj);
QByteArray ShName = "";
QByteArray retString = "";
PdfId Gobj = writer.newObject();
writer.startObj(Gobj);
PutDoc("<< /Type /Group\n");
PutDoc("/S /Transparency\n");
PutDoc("/I false\n");
PutDoc("/K false\n");
PutDoc(">>\nendobj\n");
PutDoc(">>");
writer.endObj(Gobj);
if (controlItem != NULL)
{
retString += "q\n";
3552,7 → 3647,7
}
else
{
ShName = ResNam+QString::number(ResCount);
ShName = ResNam+Pdf::toPdf(ResCount);
ResCount++;
Transpar[ShName] = writeGState("/CA "+FToStr(1.0 - trans)+"\n"
+ "/ca "+FToStr(1.0 - trans)+"\n"
3559,10 → 3654,10
+ "/SMask /None\n/AIS false\n/OPM 1\n"
+ "/BM /" + blendMode(blend) + "\n");
retString += "q\n";
retString += "/"+ShName+" gs\n";
retString += Pdf::toName(ShName) + " gs\n";
}
uint formObject = newObject();
StartObj(formObject);
PdfId formObject = writer.newObject();
writer.startObj(formObject);
PutDoc("<<\n/Type /XObject\n/Subtype /Form\n/FormType 1\n");
double bleedRight = 0.0;
double bleedLeft = 0.0;
3601,88 → 3696,39
}
else
PutDoc("/BBox [ "+FToStr(-bleedLeft)+" "+FToStr(-Options.bleeds.bottom())+" "+FToStr(maxBoxX)+" "+FToStr(maxBoxY)+" ]\n");
PutDoc("/Group "+QString::number(Gobj)+" 0 R\n");
PutDoc("/Resources << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]\n");
if ((Seite.ImgObjects.count() != 0) || (Seite.XObjects.count() != 0))
{
PutDoc("/XObject <<\n");
QMap<QString,int>::Iterator it;
for (it = Seite.ImgObjects.begin(); it != Seite.ImgObjects.end(); ++it)
PutDoc("/"+it.key()+" "+QString::number(it.value())+" 0 R\n");
QMap<QString,int>::Iterator iti;
for (iti = Seite.XObjects.begin(); iti != Seite.XObjects.end(); ++iti)
PutDoc("/"+iti.key()+" "+QString::number(iti.value())+" 0 R\n");
PutDoc(">>\n");
}
if (Seite.FObjects.count() != 0)
{
PutDoc("/Font << \n");
QMap<QString,int>::Iterator it2;
for (it2 = Seite.FObjects.begin(); it2 != Seite.FObjects.end(); ++it2)
PutDoc("/"+it2.key()+" "+QString::number(it2.value())+" 0 R\n");
PutDoc(">>\n");
}
if (Shadings.count() != 0)
{
PutDoc("/Shading << \n");
QMap<QString,int>::Iterator it3;
for (it3 = Shadings.begin(); it3 != Shadings.end(); ++it3)
PutDoc("/"+it3.key()+" "+QString::number(it3.value())+" 0 R\n");
PutDoc(">>\n");
}
if (Patterns.count() != 0)
{
PutDoc("/Pattern << \n");
QMap<QString,int>::Iterator it3p;
for (it3p = Patterns.begin(); it3p != Patterns.end(); ++it3p)
PutDoc("/"+it3p.key()+" "+QString::number(it3p.value())+" 0 R\n");
PutDoc(">>\n");
}
if (Transpar.count() != 0)
{
PutDoc("/ExtGState << \n");
QMap<QString,int>::Iterator it3t;
for (it3t = Transpar.begin(); it3t != Transpar.end(); ++it3t)
PutDoc("/"+it3t.key()+" "+QString::number(it3t.value())+" 0 R\n");
PutDoc(">>\n");
}
if ((ICCProfiles.count() != 0) || (spotMap.count() != 0))
{
PutDoc("/ColorSpace << \n");
QMap<QString,ICCD>::Iterator it3c;
if (ICCProfiles.count() != 0)
{
for (it3c = ICCProfiles.begin(); it3c != ICCProfiles.end(); ++it3c)
PutDoc("/"+it3c.value().ResName+" "+QString::number(it3c.value().ResNum)+" 0 R\n");
}
QMap<QString,SpotC>::Iterator it3sc;
if (spotMap.count() != 0)
{
for (it3sc = spotMap.begin(); it3sc != spotMap.end(); ++it3sc)
PutDoc("/"+it3sc.value().ResName+" "+QString::number(it3sc.value().ResNum)+" 0 R\n");
}
PutDoc(">>\n");
}
PutDoc(">>\n");
PutDoc("/Group "+Pdf::toObjRef(Gobj)+"\n");
PutDoc("/Resources ");
Pdf::ResourceDictionary dict;
dict.XObject.unite(pageData.ImgObjects);
dict.XObject.unite(pageData.XObjects);
dict.Font = pageData.FObjects;
dict.Shading = Shadings;
dict.Pattern = Patterns;
dict.ExtGState = Transpar;
dict.ColorSpace.append(asColorSpace(ICCProfiles.values()));
dict.ColorSpace.append(asColorSpace(spotMap.values()));
writer.write(dict);
 
if (Options.Compress)
data = CompressArray(data);
PutDoc("/Length "+Pdf::toPdf(data.length()+1));
if (Options.Compress)
data = CompressStr(&data);
PutDoc("/Length "+QString::number(data.length()+1));
if (Options.Compress)
PutDoc("\n/Filter /FlateDecode");
PutDoc(" >>\nstream\n"+EncStream(data, formObject)+"\nendstream\nendobj\n");
QString name = ResNam+QString::number(ResCount);
PutDoc(" >>\nstream\n" + EncStream(data, formObject) + "\nendstream");
writer.endObj(formObject);
QByteArray name = ResNam+Pdf::toPdf(ResCount);
ResCount++;
Seite.XObjects[name] = formObject;
retString += "/"+name+" Do\n";
pageData.XObjects[name] = formObject;
retString += Pdf::toName(name) + " Do\n";
retString += "Q\n";
return retString;
}
 
QString PDFLibCore::PDF_PutSoftShadow(PageItem* ite, const ScPage *pag)
QByteArray PDFLibCore::PDF_PutSoftShadow(PageItem* ite, const ScPage *pag)
{
if (Options.Version < PDFOptions::PDFVersion_14 || !ite->hasSoftShadow() || ite->softShadowColor() == CommonStrings::None || !ite->printEnabled())
return "";
QString tmp("q\n");
QByteArray tmp("q\n");
double softShadowDPI = Options.Resolution;
int pixelRadius = qRound(ite->softShadowBlurRadius() / 72.0 * softShadowDPI);
tmp += "1 0 0 1 ";
3742,38 → 3788,39
ImageEffect eff;
ScImageEffectList el;
eff.effectCode = ScImage::EF_BLUR;
eff.effectParameters = QString("%1 1.0").arg(pixelRadius);
eff.effectParameters = Pdf::toPdf(pixelRadius) + " 1.0";
el.append(eff);
img.applyEffect(el,ite->doc()->PageColors,false);
*/
uint maskObj = newObject();
StartObj(maskObj);
PdfId maskObj = writer.newObject();
writer.startObj(maskObj);
PutDoc("<<\n/Type /XObject\n/Subtype /Image\n");
PutDoc("/Width "+QString::number(img.width())+"\n");
PutDoc("/Height "+QString::number(img.height())+"\n");
PutDoc("/Width "+Pdf::toPdf(img.width())+"\n");
PutDoc("/Height "+Pdf::toPdf(img.height())+"\n");
PutDoc("/ColorSpace /DeviceGray\n");
PutDoc("/BitsPerComponent 8\n");
uint lengthObj = newObject();
PutDoc("/Length "+QString::number(lengthObj)+" 0 R\n");
uint lengthObj = writer.newObject();
PutDoc("/Length "+Pdf::toPdf(lengthObj)+" 0 R\n");
PutDoc("/Filter /FlateDecode\n");
PutDoc(">>\nstream\n");
int bytesWritten = WriteFlateImageToStream(img, maskObj, ColorSpaceGray, false);
PutDoc("\nendstream\nendobj\n");
StartObj(lengthObj);
PutDoc(QString(" %1\n").arg(bytesWritten));
PutDoc("endobj\n");
PutDoc("\nendstream");
writer.endObj(maskObj);
writer.startObj(lengthObj);
PutDoc(" " + Pdf::toPdf(bytesWritten));
writer.endObj(lengthObj);
 
uint colObj = newObject();
StartObj(colObj);
PdfId colObj = writer.newObject();
writer.startObj(colObj);
PutDoc("<<\n/Type /XObject\n/Subtype /Image\n");
PutDoc("/Width 1\n");
PutDoc("/Height 1\n");
PutDoc("/Interpolate false\n");
PutDoc("/BitsPerComponent 8\n");
PutDoc("/SMask "+QString::number(maskObj)+" 0 R\n");
PutDoc("/SMask "+Pdf::toPdf(maskObj)+" 0 R\n");
 
ScImage col(1,1);
QString colstr = SetColor(ite->softShadowColor(), ite->softShadowShade());
QByteArray colstr = SetColor(ite->softShadowColor(), ite->softShadowShade());
if (Options.isGrayscale)
{
double gf;
3787,7 → 3834,7
PutDoc("/Length 1\n");
PutDoc(">>\nstream\n");
WriteImageToStream(col, colObj, ColorSpaceGray, true);
PutDoc("\nendstream\nendobj\n");
PutDoc("\nendstream");
}
else if (Options.UseRGB)
{
3802,7 → 3849,7
PutDoc("/Length 3\n");
PutDoc(">>\nstream\n");
WriteImageToStream(col, colObj, ColorSpaceRGB, false);
PutDoc("\nendstream\nendobj\n");
PutDoc("\nendstream");
}
else //CMYK
{
3818,27 → 3865,27
PutDoc("/Length 4\n");
PutDoc(">>\nstream\n");
WriteImageToStream(col, colObj, ColorSpaceCMYK, false);
PutDoc("\nendstream\nendobj\n");
PutDoc("\nendstream");
}
 
QString colRes = ResNam+QString::number(ResCount);
Seite.ImgObjects[colRes] = colObj;
writer.endObj(colObj);
QByteArray colRes = ResNam+Pdf::toPdf(ResCount);
pageData.ImgObjects[colRes] = colObj;
ResCount++;
 
QString ShName = ResNam+QString::number(ResCount);
QByteArray ShName = ResNam+Pdf::toPdf(ResCount);
ResCount++;
Transpar[ShName] = writeGState("/ca "+FToStr(1.0 - ite->softShadowOpacity())+"\n"
+ "/AIS false\n/OPM 1\n"
+ "/BM /" + blendMode(ite->softShadowBlendMode()) + "\n");
 
tmp += "/"+ShName+" gs\n";
tmp += Pdf::toName(ShName) + " gs\n";
 
tmp += "/"+colRes+" Do Q\n";
tmp += Pdf::toName(colRes) + " Do Q\n";
return tmp;
}
 
/**
* Fill this.output with the QString representation of the item.
* Fill this.output with the QByteArray representation of the item.
* Checks if the item can be represented in the chosen PDF version, based on some of its caracteristics
* and delegates the work to PDF_Image, PDF_GradientFillStroke, PDF_PatternFillStroke and so on for
* filling this.output.
3845,9 → 3892,9
* Returns false if the content can't be represented in the chosen PDF version,
* if the image can't be loaded
*/
bool PDFLibCore::PDF_ProcessItem(QString& output, PageItem* ite, const ScPage* pag, uint PNr, bool embedded, bool pattern)
bool PDFLibCore::PDF_ProcessItem(QByteArray& output, PageItem* ite, const ScPage* pag, uint PNr, bool embedded, bool pattern)
{
QString tmp(""), tmpOut;
QByteArray tmp(""), tmpOut;
if (ite->isGroup())
ite->asGroupFrame()->adjustXYPosition();
ite->setRedrawBounding();
3888,12 → 3935,12
tmp += "q\n";
if ((ite->doOverprint) && (!Options.UseRGB))
{
QString ShName = ResNam+QString::number(ResCount);
QByteArray ShName = ResNam+Pdf::toPdf(ResCount);
ResCount++;
Transpar[ShName] = writeGState("/OP true\n"
"/op true\n"
"/OPM 1\n");
tmp += "/"+ShName+" gs\n";
tmp += Pdf::toName(ShName) + " gs\n";
}
// if (((ite->fillTransparency() != 0) || (ite->lineTransparency() != 0)) && (Options.Version >= PDFOptions::PDFVersion_14))
// tmp += PDF_Transparenz(ite);
3924,12 → 3971,12
// #8758: Custom dotted lines don't export properly to pdf
// Null values have to be exported if line end != flat
if ((da != 0) || (ite->lineEnd() != Qt::FlatCap))
tmp += QString::number(da)+" ";
tmp += Pdf::toPdf(da)+" ";
}
tmp += "] "+QString::number(ite->DashOffset)+" d\n";
tmp += "] "+Pdf::toPdf(ite->DashOffset)+" d\n";
}
else
tmp += "["+getDashString(ite->PLineArt, ite->lineWidth())+"] 0 d\n";
tmp += "[" + Pdf::toAscii(getDashString(ite->PLineArt, ite->lineWidth())) + "] 0 d\n";
switch (ite->PLineEnd)
{
case Qt::FlatCap:
4111,7 → 4158,7
{
if (ite->annotation().Type() == Annotation::RadioButton)
{
Seite.radioButtonList.append(ite);
pageData.radioButtonList.append(ite);
break;
}
if (!PDF_Annotation(ite, PNr))
4607,7 → 4654,7
case PageItem::Symbol:
if (doc.docPatterns.contains(ite->pattern()))
{
QString tmpD = "";
QByteArray tmpD;
ScPattern pat = doc.docPatterns[ite->pattern()];
tmp += "q\n";
tmp += SetPathAndClip(ite);
4626,7 → 4673,7
PageItem* embedded = pat.items.at(em);
tmpD += "q\n";
tmpD += "1 0 0 1 "+FToStr(embedded->gXpos)+" "+FToStr(ite->height() - embedded->gYpos)+" cm\n";
QString output;
QByteArray output;
if (!PDF_ProcessItem(output, embedded, pag, PNr, true))
return "";
tmpD += output;
4643,7 → 4690,7
case PageItem::Group:
if (ite->groupItemList.count() > 0)
{
QString tmpD = "";
QByteArray tmpD;
tmp += "q\n";
if (ite->groupClipping())
tmp += SetPathAndClip(ite);
4661,7 → 4708,7
PageItem* embedded = ite->groupItemList.at(em);
tmpD += "q\n";
tmpD += "1 0 0 1 "+FToStr(embedded->gXpos)+" "+FToStr(ite->height() - embedded->gYpos)+" cm\n";
QString output;
QByteArray output;
if (inPattern > 0)
patternStackPos.push(QPointF(embedded->gXpos, ite->height() - embedded->gYpos));
if (!PDF_ProcessItem(output, embedded, pag, PNr, true))
4872,7 → 4919,7
PageItem* textFrame = cell.textFrame();
tmp += "q\n";
tmp += "1 0 0 1 "+FToStr(cell.contentRect().x())+" "+FToStr(-cell.contentRect().y())+" cm\n";
QString output;
QByteArray output;
PDF_ProcessItem(output, textFrame, pag, PNr, true);
tmp += output;
tmp += "Q\n";
4890,9 → 4937,9
return true;
}
 
QString PDFLibCore::paintBorder(const TableBorder& border, const QPointF& start, const QPointF& end, const QPointF& startOffsetFactors, const QPointF& endOffsetFactors)
QByteArray PDFLibCore::paintBorder(const TableBorder& border, const QPointF& start, const QPointF& end, const QPointF& startOffsetFactors, const QPointF& endOffsetFactors)
{
QString tmp;
QByteArray tmp;
tmp = "";
tmp += "q\n";
QPointF lineStart, lineEnd;
4918,12 → 4965,12
{
double da = *it;
if (da != 0)
tmp += QString::number(da)+" ";
tmp += Pdf::toPdf(da)+" ";
}
tmp += "] 0 d\n";
}
else
tmp += "["+getDashString(line.style(), line.width())+"] 0 d\n";
tmp += "[" + Pdf::toAscii(getDashString(line.style(), line.width())) + "] 0 d\n";
tmp += "0 J 0 j S\n";
}
tmp += "Q\n";
4930,9 → 4977,9
return tmp;
}
 
QString PDFLibCore::handleBrushPattern(PageItem* ite, QPainterPath &path, const ScPage* pag, uint PNr)
QByteArray PDFLibCore::handleBrushPattern(PageItem* ite, QPainterPath &path, const ScPage* pag, uint PNr)
{
QString tmp;
QByteArray tmp;
tmp = "";
ScPattern pat = doc.docPatterns[ite->strokePattern()];
double pLen = path.length() - ((pat.width / 2.0) * (ite->patternStrokeScaleX / 100.0));
4974,7 → 5021,7
PageItem* embedded = pat.items.at(em);
tmp += "q\n";
tmp += "1 0 0 1 "+FToStr(embedded->gXpos)+" "+FToStr(embedded->gHeight - embedded->gYpos)+" cm\n";
QString output;
QByteArray output;
if (!PDF_ProcessItem(output, embedded, pag, PNr, true))
return "";
tmp += output;
4986,9 → 5033,9
return tmp;
}
 
QString PDFLibCore::drawArrow(PageItem *ite, QTransform &arrowTrans, int arrowIndex)
QByteArray PDFLibCore::drawArrow(PageItem *ite, QTransform &arrowTrans, int arrowIndex)
{
QString tmp = "";
QByteArray tmp = "";
FPointArray arrow = doc.arrowStyles().at(arrowIndex-1).points.copy();
if (ite->NamedLStyle.isEmpty())
{
5004,13 → 5051,13
arrow.map(arrowTrans);
if ((ite->lineTransparency() != 0) && ((Options.Version >= PDFOptions::PDFVersion_14) || (Options.Version == PDFOptions::PDFVersion_X4)))
{
QString ShName = ResNam+QString::number(ResCount);
QByteArray ShName = ResNam+Pdf::toPdf(ResCount);
ResCount++;
Transpar[ShName] = writeGState("/CA "+FToStr(1.0 - ite->lineTransparency())+"\n"
+ "/ca "+FToStr(1.0 - ite->lineTransparency())+"\n"
+ "/SMask /None\n/AIS false\n/OPM 1\n"
+ "/BM /Normal\n");
tmp += "/"+ShName+" gs\n";
tmp += Pdf::toName(ShName) + " gs\n";
}
if (ite->NamedLStyle.isEmpty())
{
5017,7 → 5064,7
if (!ite->strokePattern().isEmpty())
{
tmp += SetClipPathArray(&arrow);
QString tmpOut;
QByteArray tmpOut;
PDF_PatternFillStroke(tmpOut, ite, 1, true);
tmp += tmpOut;
tmp += "h\nf*\n";
5025,7 → 5072,7
else if (ite->GrTypeStroke > 0)
{
tmp += SetClipPathArray(&arrow);
QString tmpOut;
QByteArray tmpOut;
PDF_GradientFillStroke(tmpOut, ite, true, true);
tmp += "q\n";
tmp += tmpOut;
5060,10 → 5107,10
return tmp;
}
 
QString PDFLibCore::putColor(const QString& color, double shade, bool fill)
QByteArray PDFLibCore::putColor(const QString& color, double shade, bool fill)
{
QString tmp = "";
QString colString = SetColor(color, shade);
QByteArray tmp = "";
QByteArray colString = SetColor(color, shade);
ScColor tmpC;
tmpC = doc.PageColors[color];
if (((tmpC.isSpotColor()) || (tmpC.isRegistrationColor())) && ((Options.isGrayscale == false) && (Options.UseRGB == false)) && (Options.UseSpotColors))
5120,7 → 5167,7
}
else
{
QString tmp2[] = {"/Perceptual", "/RelativeColorimetric", "/Saturation", "/AbsoluteColorimetric"};
QByteArray tmp2[] = {"/Perceptual", "/RelativeColorimetric", "/Saturation", "/AbsoluteColorimetric"};
tmp += tmp2[Options.Intent]+ " ri\n";
if (color != CommonStrings::None)
{
5152,7 → 5199,7
}
 
/*CB 2982: cache code is borked somehow, original function is above
QString PDFLibCore::putColor(const QString & colorName, int shade, bool fill)
QByteArray PDFLibCore::putColor(const QString & colorName, int shade, bool fill)
{
// Cache of last foreground and background colours We cache fg and bg
// separately because they're alternated so much. The primary purpose of
5182,12 → 5229,12
}
*/
 
QString PDFLibCore::putColorUncached(const QString& color, int shade, bool fill)
QByteArray PDFLibCore::putColorUncached(const QString& color, int shade, bool fill)
{
ScColor tmpC(doc.PageColors[color]);
if (((tmpC.isSpotColor()) || (tmpC.isRegistrationColor())) && ((Options.isGrayscale == false) && (Options.UseRGB == false)) && (Options.UseSpotColors))
{
QString tmpSpot("");
QByteArray tmpSpot("");
if ((color != CommonStrings::None) && (spotMap.contains(color)))
{
if (fill)
5203,10 → 5250,10
}
return tmpSpot;
}
QString colString(SetColor(color, shade));
QByteArray colString(SetColor(color, shade));
if (Options.isGrayscale)
{
QString tmpGray("");
QByteArray tmpGray("");
if (color != CommonStrings::None)
{
if (fill)
5216,7 → 5263,7
}
return tmpGray;
}
QString tmp("");
QByteArray tmp("");
if (Options.UseRGB)
{
if (color != CommonStrings::None)
5243,7 → 5290,7
}
else
{
QString tmp2[] = {"/Perceptual", "/RelativeColorimetric", "/Saturation", "/AbsoluteColorimetric"};
QByteArray tmp2[] = {"/Perceptual", "/RelativeColorimetric", "/Saturation", "/AbsoluteColorimetric"};
tmp += tmp2[Options.Intent]+ " ri\n";
if (color != CommonStrings::None)
{
5274,14 → 5321,14
return tmp;
}
 
QString PDFLibCore::setStrokeMulti(struct SingleLine *sl)
QByteArray PDFLibCore::setStrokeMulti(struct SingleLine *sl)
{
QString tmp(
QByteArray tmp(
putColor(sl->Color, sl->Shade, false) +
FToStr(sl->Width)+" w\n"
);
QString Ds = getDashString(sl->Dash, sl->Width);
tmp += Ds.isEmpty() ? "[] 0 d\n" : QString("[%1] 0 d\n").arg(Ds);
QByteArray Ds = Pdf::toAscii(getDashString(sl->Dash, sl->Width));
tmp += Ds.isEmpty() ? "[] 0 d\n" : "[" + Ds + "] 0 d\n";
switch (static_cast<Qt::PenCapStyle>(sl->LineEnd))
{
case Qt::FlatCap:
5316,12 → 5363,12
}
 
// Return a PDF substring representing a PageItem's text
QString PDFLibCore::setTextSt(PageItem *ite, uint PNr, const ScPage* pag)
QByteArray PDFLibCore::setTextSt(PageItem *ite, uint PNr, const ScPage* pag)
{
int tabCc = 0;
int savedOwnPage = ite->OwnPage;
double tabDist = ite->textToFrameDistLeft();
QString tmp(""), tmp2("");
QByteArray tmp(""), tmp2("");
QList<ParagraphStyle::TabRecord> tTabValues;
ite->OwnPage = PNr;
ite->layout();
5541,11 → 5588,11
return tmp;
}
 
bool PDFLibCore::setTextCh(PageItem *ite, uint PNr, double x, double y, uint d, QString &tmp, QString &tmp2, const CharStyle& style, GlyphLayout *glyphs, PathData* pdata, const ParagraphStyle& pstyle, const ScPage* pag)
bool PDFLibCore::setTextCh(PageItem *ite, uint PNr, double x, double y, uint d, QByteArray &tmp, QByteArray &tmp2, const CharStyle& style, GlyphLayout *glyphs, PathData* pdata, const ParagraphStyle& pstyle, const ScPage* pag)
{
QString output;
QString FillColor = "";
QString StrokeColor = "";
QByteArray output;
QByteArray FillColor = "";
QByteArray StrokeColor = "";
if (ite->asPathText())
{
tmp += "q\n";
5639,6 → 5686,7
return true;
}
 
PdfFont pdfFont = UsedFontsP[style.font().replacementName()];
uint glyph = glyphs->glyph;
 
if (glyph == (ScFace::CONTROL_GLYPHS + SpecialChars::NBSPACE.unicode()) ||
5719,7 → 5767,8
}
tmp2 += "S\n";
}
if (Options.OutlineList.contains(style.font().replacementName()))
if (pdfFont.method == Use_XForm)
{
if (glyph != style.font().char2CMap(QChar(' ')))
{
5769,7 → 5818,8
tmp2 += "1 0 0 1 0 "+FToStr( (((tsz / 10.0) - (tsz / 10.0) * (glyphs->scaleV)) / (tsz / 10.0)) * -1)+" cm\n";
tmp2 += FToStr(qMax(glyphs->scaleH, 0.1))+" 0 0 "+FToStr(qMax(glyphs->scaleV, 0.1))+" 0 0 cm\n";
if (style.fillColor() != CommonStrings::None)
tmp2 += "/"+style.font().psName().replace( QRegExp("[\\s\\/\\{\\[\\]\\}\\<\\>\\(\\)\\%]"), "_" )+QString::number(glyph)+" Do\n";
tmp2 += pdfFont.name + Pdf::toPdf(glyph)+" Do\n";
if (style.effects() & ScStyle_Outline)
{
FPointArray gly = style.font().glyphOutline(glyph);
5821,24 → 5871,34
}
if (glyph != style.font().char2CMap(QChar(' ')))
{
uint idx = glyphs->glyph;
uint idx1;
if (Options.SubsetList.contains(style.font().replacementName()))
idx1 = Type3Fonts[UsedFontsP[style.font().replacementName()]][idx] / 256;
uint gid = glyphs->glyph;
uint fontNr = 65535;
switch (pdfFont.encoding)
{
case Encode_256:
gid = pdfFont.glyphmap[gid];
fontNr = gid / 256;
gid = gid % 256;
break;
case Encode_224:
fontNr = gid / 224;
gid = gid % 224 + 32;
break;
case Encode_Subset:
gid = pdfFont.glyphmap[gid];
break;
case Encode_IdentityH:
break;
}
if (fontNr == 65535)
tmp+= pdfFont.name + " " + FToStr(tsz / 10.0)+" Tf\n";
else
idx1 = idx / 224;
ScFace currentFace = style.font();
if ((Options.Version == PDFOptions::PDFVersion_X4 || currentFace.isSymbolic() || !currentFace.hasNames())
&& (currentFace.format() == ScFace::SFNT || currentFace.format() == ScFace::TTCF)
&& ( !Options.SubsetList.contains(style.font().replacementName()) ) )
tmp+= UsedFontsP[currentFace.replacementName()]+" "+FToStr(tsz / 10.0)+" Tf\n";
else
tmp += UsedFontsP[style.font().replacementName()]+"S"+QString::number(idx1)+" "+FToStr(tsz / 10.0)+" Tf\n";
tmp += pdfFont.name+"S"+Pdf::toPdf(fontNr) + " "+FToStr(tsz / 10.0)+" Tf\n";
if (style.strokeColor() != CommonStrings::None)
tmp += StrokeColor;
if (style.fillColor() != CommonStrings::None)
tmp += FillColor;
if ((Options.SubsetList.contains(style.font().replacementName())) && (style.effects() & ScStyle_Outline) && (style.strokeColor() != CommonStrings::None))
if (pdfFont.method == Use_Type3 && (style.effects() & ScStyle_Outline) && (style.strokeColor() != CommonStrings::None))
{
tmp2 += "q\n";
tmp2 += FToStr((tsz * style.outlineWidth() / 1000.0) / tsz)+" w\n[] 0 d\n0 J\n0 j\n";
5877,6 → 5937,7
if (glyphs->scaleV != 1.0)
tmp2 += "1 0 0 1 0 "+FToStr( (((tsz / 10.0) - (tsz / 10.0) * (glyphs->scaleV)) / (tsz / 10.0)) * -1)+" cm\n";
tmp2 += FToStr(qMax(glyphs->scaleH, 0.1))+" 0 0 "+FToStr(qMax(glyphs->scaleV, 0.1))+" 0 0 cm\n";
/* paint outline */
FPointArray gly = style.font().glyphOutline(glyph);
QTransform mat;
mat.scale(0.1, 0.1);
5928,31 → 5989,23
tmp += FToStr(qMax(glyphs->scaleH, 0.1))+" 0 0 "+FToStr(qMax(glyphs->scaleV, 0.1))+" "+FToStr(x+ glyphs->xoffset)+" "+FToStr(-y- glyphs->yoffset+(style.fontSize() / 10.0) * (style.baselineOffset() / 1000.0))+" Tm\n";
}
else
tmp += FToStr(qMax(glyphs->scaleH, 0.1))+" 0 0 "+FToStr(qMax(glyphs->scaleV, 0.1))+" 0 0 Tm\n";
uchar idx2;
if (Options.SubsetList.contains(style.font().replacementName()))
{
if (style.fillColor() != CommonStrings::None)
{
idx2 = Type3Fonts[UsedFontsP[style.font().replacementName()]][idx] % 256;
tmp += "<"+QString(toHex(idx2))+"> Tj\n";
}
}
else if ((Options.Version == PDFOptions::PDFVersion_X4 || currentFace.isSymbolic() || !currentFace.hasNames()) &&
(currentFace.format() == ScFace::SFNT || currentFace.format() == ScFace::TTCF))
{
QString val;
val.setNum(idx,16);
int numberOfZero = 4-val.size();
for (int i=0; i<numberOfZero; ++i)
val.prepend("0");
tmp += "<"+val+"> Tj\n";
}
else
{
idx2 = idx % 224 + 32;
tmp += "<"+QString(toHex(idx2))+"> Tj\n";
}
{
tmp += FToStr(qMax(glyphs->scaleH, 0.1))+" 0 0 "+FToStr(qMax(glyphs->scaleV, 0.1))+" 0 0 Tm\n";
}
if (pdfFont.method != Use_Type3 || style.fillColor() != CommonStrings::None)
{
switch (pdfFont.encoding)
{
case Encode_224:
case Encode_256:
tmp += Pdf::toHexString8(gid) + " Tj\n";
break;
case Encode_IdentityH:
default:
tmp += Pdf::toHexString16(gid) + " Tj\n";
break;
}
}
}
}
if ((style.effects() & ScStyle_Strikethrough) && (chstr != SpecialChars::PARSEP))
6011,15 → 6064,15
return true;
}
 
QString PDFLibCore::SetColor(const QString& farbe, double Shade)
QByteArray PDFLibCore::SetColor(const QString& farbe, double Shade)
{
const ScColor& col = doc.PageColors[farbe];
return SetColor(col, Shade);
}
 
QString PDFLibCore::SetColor(const ScColor& farbe, double Shade)
QByteArray PDFLibCore::SetColor(const ScColor& farbe, double Shade)
{
QString tmp;
QByteArray tmp;
RGBColor rgb;
CMYKColor cmyk;
int h, s, v, k;
6086,9 → 6139,9
return tmp;
}
 
QString PDFLibCore::SetGradientColor(const QString& farbe, double Shade)
QByteArray PDFLibCore::SetGradientColor(const QString& farbe, double Shade)
{
QString tmp;
QByteArray tmp;
RGBColor rgb;
CMYKColor cmyk;
int h, s, v, k;
6166,9 → 6219,9
return tmp;
}
 
QString PDFLibCore::SetClipPath(PageItem *ite, bool poly)
QByteArray PDFLibCore::SetClipPath(PageItem *ite, bool poly)
{
QString tmp;
QByteArray tmp;
FPoint np, np1, np2, np3, np4, firstP;
bool nPath = true;
bool first = true;
6210,9 → 6263,9
return tmp;
}
 
QString PDFLibCore::SetClipPathArray(FPointArray *ite, bool poly)
QByteArray PDFLibCore::SetClipPathArray(FPointArray *ite, bool poly)
{
QString tmp;
QByteArray tmp;
FPoint np, np1, np2, np3, np4, firstP;
bool nPath = true;
bool first = true;
6254,9 → 6307,9
return tmp;
}
 
QString PDFLibCore::SetClipPathImage(PageItem *ite)
QByteArray PDFLibCore::SetClipPathImage(PageItem *ite)
{
QString tmp;
QByteArray tmp;
if (ite->imageClip.size() <= 3)
return tmp;
 
6292,25 → 6345,25
return tmp;
}
 
QString PDFLibCore::SetImagePathAndClip(PageItem *item)
QByteArray PDFLibCore::SetImagePathAndClip(PageItem *item)
{
QString tmp = SetClipPathImage(item);
QByteArray tmp = SetClipPathImage(item);
if (tmp.length() > 0)
tmp += "h W* n\n";
return tmp;
}
 
QString PDFLibCore::SetPathAndClip(PageItem *item)
QByteArray PDFLibCore::SetPathAndClip(PageItem *item)
{
QString tmp = SetClipPath(item);
QByteArray tmp = SetClipPath(item);
if (tmp.length() > 0)
tmp += (item->fillRule ? "h W* n\n" : "h W n\n");
return tmp;
}
 
QString PDFLibCore::SetPathAndClip(PageItem *item, bool fillRule)
QByteArray PDFLibCore::SetPathAndClip(PageItem *item, bool fillRule)
{
QString tmp = SetClipPath(item);
QByteArray tmp = SetClipPath(item);
if (tmp.length() > 0)
tmp += (fillRule ? "h W* n\n" : "h W n\n");
return tmp;
6334,12 → 6387,12
top = values.top();
}
 
QString PDFLibCore::PDF_TransparenzFill(PageItem *currItem)
QByteArray PDFLibCore::PDF_TransparenzFill(PageItem *currItem)
{
QString ShName = ResNam+QString::number(ResCount);
QByteArray ShName = ResNam+Pdf::toPdf(ResCount);
ResCount++;
QString tmp;
QString GXName;
QByteArray tmp;
QByteArray GXName;
double scaleX = 1.0;
double scaleY = 1.0;
if (currItem->itemType() == PageItem::Symbol)
6434,9 → 6487,9
TransVec.append(a);
}
}
QString TRes("");
uint patObject = newObject();
StartObj(patObject);
QByteArray TRes("");
PdfId patObject = writer.newObject();
writer.startObj(patObject);
PutDoc("<<\n/Type /Pattern\n");
PutDoc("/PatternType 2\n");
PutDoc("/Matrix ["+FToStr(mpa.m11())+" "+FToStr(mpa.m12())+" "+FToStr(mpa.m21())+" "+FToStr(mpa.m22())+" "+FToStr(mpa.dx())+" "+FToStr(mpa.dy())+"]\n");
6462,7 → 6515,7
if (StopVec.count() > 2)
{
PutDoc("/Bounds [");
QString bctx = "";
QByteArray bctx = "";
for (int bc = 1; bc < StopVec.count() - 1; bc++)
{
bctx += FToStr(StopVec.at(bc))+" ";
6471,7 → 6524,7
}
else
PutDoc("/Bounds []\n");
QString entx = "";
QByteArray entx = "";
PutDoc("/Functions\n");
PutDoc("[\n");
for (int cc = 0; cc < TransVec.count() - 1; cc++)
6490,9 → 6543,11
PutDoc(">>\n");
PutDoc(">>\n");
PutDoc(">>\n");
Patterns.insert("Pattern"+QString::number(patObject), patObject);
uint formObject = newObject();
StartObj(formObject);
writer.endObj(patObject);
Patterns.insert("Pattern"+Pdf::toPdf(patObject), patObject);
PdfId formObject = writer.newObject();
writer.startObj(formObject);
PutDoc("<<\n/Type /XObject\n/Subtype /Form\n");
PutDoc("/FormType 1\n");
PutDoc("/Group << /S /Transparency /CS /DeviceGray >>\n");
6501,28 → 6556,15
PutDoc("/BBox [0 0 "+FToStr(ActPageP->width())+" "+FToStr(ActPageP->height())+" ]\n");
else
PutDoc("/BBox ["+FToStr(-lw / 2.0)+" "+FToStr(lw / 2.0)+" "+FToStr(currItem->width()+lw)+" "+FToStr(-(currItem->height()+lw))+" ]\n");
PutDoc("/Resources << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]\n");
if (Patterns.count() != 0)
{
PutDoc("/Pattern << \n");
QMap<QString,int>::Iterator it3p;
for (it3p = Patterns.begin(); it3p != Patterns.end(); ++it3p)
PutDoc("/"+it3p.key()+" "+QString::number(it3p.value())+" 0 R\n");
PutDoc(">>\n");
}
if ((Seite.ImgObjects.count() != 0) || (Seite.XObjects.count() != 0))
{
PutDoc("/XObject <<\n");
QMap<QString,int>::Iterator it;
for (it = Seite.ImgObjects.begin(); it != Seite.ImgObjects.end(); ++it)
PutDoc("/"+it.key()+" "+QString::number(it.value())+" 0 R\n");
QMap<QString,int>::Iterator iti;
for (iti = Seite.XObjects.begin(); iti != Seite.XObjects.end(); ++iti)
PutDoc("/"+iti.key()+" "+QString::number(iti.value())+" 0 R\n");
PutDoc(">>\n");
}
PutDoc(">>\n");
QString stre = "q\n";
PutDoc("/Resources ");
Pdf::ResourceDictionary dict;
dict.Pattern = Patterns;
dict.XObject.unite(pageData.ImgObjects);
dict.XObject.unite(pageData.XObjects);
writer.write(dict);
PutDoc("\n");
QByteArray stre = "q\n";
if (currItem->isGroup())
{
QTransform mpa;
6540,27 → 6582,28
stre += SetClipPath(currItem)+"h\n";
stre += FToStr(fabs(currItem->lineWidth()))+" w\n";
stre += "/Pattern cs\n";
stre += "/Pattern"+QString::number(patObject)+" scn\nf*\n";
stre += "/Pattern"+Pdf::toPdf(patObject)+" scn\nf*\n";
stre += "Q\n";
if (Options.Compress)
stre = CompressStr(&stre);
PutDoc("/Length "+QString::number(stre.length())+"\n");
stre = CompressArray(stre);
PutDoc("/Length "+Pdf::toPdf(stre.length())+"\n");
if (Options.Compress)
PutDoc("/Filter /FlateDecode\n");
PutDoc(">>\nstream\n"+EncStream(stre, formObject)+"\nendstream\nendobj\n");
Seite.XObjects[ResNam+QString::number(ResCount)] = formObject;
PutDoc(">>\nstream\n"+EncStream(stre, formObject)+"\nendstream");
writer.endObj(formObject);
pageData.XObjects[ResNam+Pdf::toPdf(ResCount)] = formObject;
ResCount++;
GXName = ResNam+QString::number(ResCount);
GXName = ResNam+Pdf::toPdf(ResCount);
ResCount++;
Transpar[GXName] = writeGState("/SMask << /S /Luminosity /G "+QString::number(formObject)+" 0 R >>\n/AIS false\n/BM /" + blendMode(currItem->fillBlendmode()) + "\n");
tmp = "/"+GXName+" gs\n";
Transpar[GXName] = writeGState("/SMask << /S /Luminosity /G "+Pdf::toPdf(formObject)+" 0 R >>\n/AIS false\n/BM /" + blendMode(currItem->fillBlendmode()) + "\n");
tmp = Pdf::toName(GXName) + " gs\n";
}
else if ((currItem->GrMask == 3) || (currItem->GrMask == 6) || (currItem->GrMask == 7) || (currItem->GrMask == 8))
{
QString tmpOut = "";
QByteArray tmpOut = "";
PDF_PatternFillStroke(tmpOut, currItem, 2);
uint formObject = newObject();
StartObj(formObject);
PdfId formObject = writer.newObject();
writer.startObj(formObject);
PutDoc("<<\n/Type /XObject\n/Subtype /Form\n");
PutDoc("/FormType 1\n");
PutDoc("/Group << /S /Transparency ");
6583,28 → 6626,15
PutDoc("/BBox [0 0 "+FToStr(ActPageP->width())+" "+FToStr(ActPageP->height())+" ]\n");
else
PutDoc("/BBox [0 0 "+FToStr(currItem->width())+" "+FToStr(-(currItem->height()))+" ]\n");
PutDoc("/Resources << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]\n");
if (Patterns.count() != 0)
{
PutDoc("/Pattern << \n");
QMap<QString,int>::Iterator it3p;
for (it3p = Patterns.begin(); it3p != Patterns.end(); ++it3p)
PutDoc("/"+it3p.key()+" "+QString::number(it3p.value())+" 0 R\n");
PutDoc(">>\n");
}
if ((Seite.ImgObjects.count() != 0) || (Seite.XObjects.count() != 0))
{
PutDoc("/XObject <<\n");
QMap<QString,int>::Iterator it;
for (it = Seite.ImgObjects.begin(); it != Seite.ImgObjects.end(); ++it)
PutDoc("/"+it.key()+" "+QString::number(it.value())+" 0 R\n");
QMap<QString,int>::Iterator iti;
for (iti = Seite.XObjects.begin(); iti != Seite.XObjects.end(); ++iti)
PutDoc("/"+iti.key()+" "+QString::number(iti.value())+" 0 R\n");
PutDoc(">>\n");
}
PutDoc(">>\n");
QString stre = "q\n";
PutDoc("/Resources ");
Pdf::ResourceDictionary dict;
dict.Pattern = Patterns;
dict.XObject.unite(pageData.ImgObjects);
dict.XObject.unite(pageData.XObjects);
writer.write(dict);
PutDoc("\n");
QByteArray stre = "q\n";
if ((currItem->isGroup()) || (currItem->itemType() == PageItem::Symbol))
{
QTransform mpa;
6618,24 → 6648,25
stre += tmpOut+" f*\n";
stre += "Q\n";
if (Options.Compress)
stre = CompressStr(&stre);
PutDoc("/Length "+QString::number(stre.length())+"\n");
stre = CompressArray(stre);
PutDoc("/Length "+Pdf::toPdf(stre.length())+"\n");
if (Options.Compress)
PutDoc("/Filter /FlateDecode\n");
PutDoc(">>\nstream\n"+EncStream(stre, formObject)+"\nendstream\nendobj\n");
Seite.XObjects[ResNam+QString::number(ResCount)] = formObject;
PutDoc(">>\nstream\n"+EncStream(stre, formObject)+"\nendstream");
writer.endObj(formObject);
pageData.XObjects[ResNam+Pdf::toPdf(ResCount)] = formObject;
ResCount++;
GXName = ResNam+QString::number(ResCount);
GXName = ResNam+Pdf::toPdf(ResCount);
ResCount++;
if (currItem->GrMask == 6)
Transpar[GXName] = writeGState("/SMask << /S /Luminosity /G "+QString::number(formObject)+" 0 R >>\n/BM /" + blendMode(currItem->fillBlendmode()) + "\n");
Transpar[GXName] = writeGState("/SMask << /S /Luminosity /G "+Pdf::toPdf(formObject)+" 0 R >>\n/BM /" + blendMode(currItem->fillBlendmode()) + "\n");
else if (currItem->GrMask == 7)
Transpar[GXName] = writeGState("/SMask << /S /Luminosity /G "+QString::number(formObject)+" 0 R /BC [ 1 1 1 ] /TR << /FunctionType 2 /Domain [ 0 1 ] /Range [ 0 1 ] /C0 [ 1 ] /C1 [ 0 ] /N 1 >> >>\n/BM /" + blendMode(currItem->fillBlendmode()) + "\n/AIS true\n");
Transpar[GXName] = writeGState("/SMask << /S /Luminosity /G "+Pdf::toPdf(formObject)+" 0 R /BC [ 1 1 1 ] /TR << /FunctionType 2 /Domain [ 0 1 ] /Range [ 0 1 ] /C0 [ 1 ] /C1 [ 0 ] /N 1 >> >>\n/BM /" + blendMode(currItem->fillBlendmode()) + "\n/AIS true\n");
else if (currItem->GrMask == 3)
Transpar[GXName] = writeGState("/SMask << /S /Alpha /G "+QString::number(formObject)+" 0 R >>\n/BM /" + blendMode(currItem->fillBlendmode()) + "\n");
Transpar[GXName] = writeGState("/SMask << /S /Alpha /G "+Pdf::toPdf(formObject)+" 0 R >>\n/BM /" + blendMode(currItem->fillBlendmode()) + "\n");
else if (currItem->GrMask == 8)
Transpar[GXName] = writeGState("/SMask << /S /Alpha /G "+QString::number(formObject)+" 0 R /BC [ 1 1 1 ] /TR << /FunctionType 2 /Domain [ 0 1 ] /Range [ 0 1 ] /C0 [ 1 ] /C1 [ 0 ] /N 1 >> >>\n/BM /" + blendMode(currItem->fillBlendmode()) + "\n");
tmp = "/"+GXName+" gs\n";
Transpar[GXName] = writeGState("/SMask << /S /Alpha /G "+Pdf::toPdf(formObject)+" 0 R /BC [ 1 1 1 ] /TR << /FunctionType 2 /Domain [ 0 1 ] /Range [ 0 1 ] /C0 [ 1 ] /C1 [ 0 ] /N 1 >> >>\n/BM /" + blendMode(currItem->fillBlendmode()) + "\n");
tmp = Pdf::toName(GXName)+" gs\n";
}
else
{
6643,23 → 6674,22
Transpar[ShName] = writeGState("/CA "+FToStr(1.0 - currItem->fillTransparency())+"\n/ca "+FToStr(1.0 - currItem->fillTransparency())+"\n/SMask /None\n/AIS false\n/OPM 1\n/BM /" + blendMode(currItem->fillBlendmode()) + "\n");
else
Transpar[ShName] = writeGState("/ca "+FToStr(1.0 - currItem->fillTransparency())+"\n/SMask /None\n/AIS false\n/OPM 1\n/BM /" + blendMode(currItem->fillBlendmode()) + "\n");
tmp = "/"+ShName+" gs\n";
tmp = Pdf::toName(ShName) + " gs\n";
}
return tmp;
}
 
QString PDFLibCore::PDF_TransparenzStroke(PageItem *currItem)
QByteArray PDFLibCore::PDF_TransparenzStroke(PageItem *currItem)
{
QString ShName = ResNam+QString::number(ResCount);
QByteArray ShName = ResNam+Pdf::toPdf(ResCount);
ResCount++;
Transpar[ShName] = writeGState("/CA "+FToStr(1.0 - currItem->lineTransparency())+"\n"
+ "/SMask /None\n/AIS false\n/OPM 1\n"
+ "/BM /" + blendMode(currItem->lineBlendmode()) + "\n");
QString tmp("/"+ShName+" gs\n");
return tmp;
return Pdf::toName(ShName) + " gs\n";
}
 
bool PDFLibCore::PDF_HatchFill(QString& output, PageItem *currItem)
bool PDFLibCore::PDF_HatchFill(QByteArray& output, PageItem *currItem)
{
output += "q\n1 w\n[] 0 d\n0 J\n0 j\n";
if ((currItem->hatchBackground != CommonStrings::None) && (currItem->hatchUseBackground))
6742,9 → 6772,9
return true;
}
 
bool PDFLibCore::PDF_PatternFillStroke(QString& output, PageItem *currItem, int kind, bool forArrow)
bool PDFLibCore::PDF_PatternFillStroke(QByteArray& output, PageItem *currItem, int kind, bool forArrow)
{
QString tmp2 = "", tmpOut;
QByteArray tmp2 = "", tmpOut;
ScPattern *pat = NULL;
if (kind == 0)
{
6780,9 → 6810,9
tmp2 += "Q\n";
}
if (Options.Compress)
tmp2 = CompressStr(&tmp2);
uint patObject = newObject();
StartObj(patObject);
tmp2 = CompressArray(tmp2);
PdfId patObject = writer.newObject();
writer.startObj(patObject);
PutDoc("<< /Type /Pattern\n");
PutDoc("/PatternType 1\n");
PutDoc("/PaintType 1\n");
6849,83 → 6879,35
PutDoc("/Matrix ["+FToStr(mpa.m11())+" "+FToStr(mpa.m12())+" "+FToStr(mpa.m21())+" "+FToStr(mpa.m22())+" "+FToStr(mpa.dx())+" "+FToStr(mpa.dy())+"]\n");
PutDoc("/XStep "+FToStr(pat->width)+"\n");
PutDoc("/YStep "+FToStr(pat->height)+"\n");
PutDoc("/Resources << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]\n");
if ((Seite.ImgObjects.count() != 0) || (Seite.XObjects.count() != 0))
{
PutDoc("/XObject <<\n");
QMap<QString,int>::Iterator it;
for (it = Seite.ImgObjects.begin(); it != Seite.ImgObjects.end(); ++it)
PutDoc("/"+it.key()+" "+QString::number(it.value())+" 0 R\n");
QMap<QString,int>::Iterator iti;
for (iti = Seite.XObjects.begin(); iti != Seite.XObjects.end(); ++iti)
PutDoc("/"+iti.key()+" "+QString::number(iti.value())+" 0 R\n");
PutDoc(">>\n");
}
if (Seite.FObjects.count() != 0)
{
PutDoc("/Font << \n");
QMap<QString,int>::Iterator it2;
for (it2 = Seite.FObjects.begin(); it2 != Seite.FObjects.end(); ++it2)
PutDoc("/"+it2.key()+" "+QString::number(it2.value())+" 0 R\n");
PutDoc(">>\n");
}
if (Shadings.count() != 0)
{
PutDoc("/Shading << \n");
QMap<QString,int>::Iterator it3;
for (it3 = Shadings.begin(); it3 != Shadings.end(); ++it3)
PutDoc("/"+it3.key()+" "+QString::number(it3.value())+" 0 R\n");
PutDoc(">>\n");
}
if (Patterns.count() != 0)
{
PutDoc("/Pattern << \n");
QMap<QString,int>::Iterator it3p;
for (it3p = Patterns.begin(); it3p != Patterns.end(); ++it3p)
PutDoc("/"+it3p.key()+" "+QString::number(it3p.value())+" 0 R\n");
PutDoc(">>\n");
}
if (Transpar.count() != 0)
{
PutDoc("/ExtGState << \n");
QMap<QString,int>::Iterator it3t;
for (it3t = Transpar.begin(); it3t != Transpar.end(); ++it3t)
PutDoc("/"+it3t.key()+" "+QString::number(it3t.value())+" 0 R\n");
PutDoc(">>\n");
}
if ((ICCProfiles.count() != 0) || (spotMap.count() != 0))
{
PutDoc("/ColorSpace << \n");
QMap<QString,ICCD>::Iterator it3c;
if (ICCProfiles.count() != 0)
{
for (it3c = ICCProfiles.begin(); it3c != ICCProfiles.end(); ++it3c)
PutDoc("/"+it3c.value().ResName+" "+QString::number(it3c.value().ResNum)+" 0 R\n");
}
QMap<QString,SpotC>::Iterator it3sc;
if (spotMap.count() != 0)
{
for (it3sc = spotMap.begin(); it3sc != spotMap.end(); ++it3sc)
PutDoc("/"+it3sc.value().ResName+" "+QString::number(it3sc.value().ResNum)+" 0 R\n");
}
PutDoc(">>\n");
}
PutDoc(">>\n");
PutDoc("/Length "+QString::number(tmp2.length()));
PutDoc("/Resources ");
Pdf::ResourceDictionary dict;
dict.XObject.unite(pageData.ImgObjects);
dict.XObject.unite(pageData.XObjects);
dict.Font = pageData.FObjects;
dict.Shading = Shadings;
dict.Pattern = Patterns;
dict.ExtGState = Transpar;
dict.ColorSpace.append(asColorSpace(ICCProfiles.values()));
dict.ColorSpace.append(asColorSpace(spotMap.values()));
writer.write(dict);
PutDoc("/Length "+Pdf::toPdf(tmp2.length()));
if (Options.Compress)
PutDoc("\n/Filter /FlateDecode");
PutDoc(" >>\nstream\n"+EncStream(tmp2, patObject)+"\nendstream\nendobj\n");
Patterns.insert("Pattern"+QString::number(patObject), patObject);
QString tmp;
PutDoc(" >>\nstream\n"+EncStream(tmp2, patObject)+"\nendstream");
writer.endObj(patObject);
Patterns.insert("Pattern"+Pdf::toPdf(patObject), patObject);
QByteArray tmp;
if ((forArrow) || (kind != 1))
{
tmp = "/Pattern cs\n";
tmp += "/Pattern"+QString::number(patObject)+" scn\n";
tmp += "/Pattern"+Pdf::toPdf(patObject)+" scn\n";
}
else
{
tmp = "/Pattern CS\n";
tmp += "/Pattern"+QString::number(patObject)+" SCN\n";
tmp += "/Pattern"+Pdf::toPdf(patObject)+" SCN\n";
}
ResCount++;
output = tmp;
6961,7 → 6943,7
}
else
{
QStringList gcol = SetGradientColor(colName, colShade).split(" ");
QList<QByteArray> gcol = SetGradientColor(colName, colShade).split(' ');
for (int gcs = 0; gcs < gcol.count(); gcs++)
{
vs << encode16dVal(gcol[gcs].toDouble());
6974,7 → 6956,7
}
else
{
QStringList gcol = SetGradientColor(colName, colShade).split(" ");
QList<QByteArray> gcol = SetGradientColor(colName, colShade).split(' ');
for (int gcs = 0; gcs < gcol.count(); gcs++)
{
vs << encode16dVal(gcol[gcs].toDouble());
6982,7 → 6964,7
}
}
 
bool PDFLibCore::PDF_MeshGradientFill(QString& output, PageItem *c)
bool PDFLibCore::PDF_MeshGradientFill(QByteArray& output, PageItem *c)
{
QList<double> StopVec;
QList<double> TransVec;
7029,11 → 7011,11
Gcolors.append(SetGradientColor(mp1.colorName, mp1.shade));
}
}
QString TRes("");
QByteArray TRes;
if (((Options.Version >= PDFOptions::PDFVersion_14) || (Options.Version == PDFOptions::PDFVersion_X4)) && (transparencyFound))
{
uint shadeObjectT = newObject();
StartObj(shadeObjectT);
PdfId shadeObjectT = writer.newObject();
writer.startObj(shadeObjectT);
PutDoc("<<\n");
PutDoc("/ShadingType 7\n");
PutDoc("/ColorSpace /DeviceGray\n");
7069,63 → 7051,63
}
}
PutDoc("/Decode [-40000 40000 -40000 40000 0 1]\n");
QString dat = "";
QByteArray dat = "";
for (int vd = 0; vd < vertStreamT.count(); vd++)
{
dat += vertStreamT[vd];
}
if (Options.Compress)
dat = CompressStr(&dat);
PutDoc("/Length "+QString::number(dat.length())+"\n");
dat = CompressArray(dat);
PutDoc("/Length "+Pdf::toPdf(dat.length())+"\n");
if (Options.Compress)
PutDoc("/Filter /FlateDecode\n");
PutDoc(">>\nstream\n"+EncStream(dat, shadeObjectT)+"\nendstream\nendobj\n");
uint patObject = newObject();
StartObj(patObject);
PutDoc(">>\nstream\n"+EncStream(dat, shadeObjectT)+"\nendstream");
writer.endObj(shadeObjectT);
PdfId patObject = writer.newObject();
writer.startObj(patObject);
PutDoc("<<\n/Type /Pattern\n");
PutDoc("/PatternType 2\n");
PutDoc("/Shading "+QString::number(shadeObjectT)+" 0 R\n");
PutDoc(">>\nendobj\n");
Patterns.insert("Pattern"+QString::number(patObject), patObject);
uint formObject = newObject();
StartObj(formObject);
PutDoc("/Shading "+Pdf::toPdf(shadeObjectT)+" 0 R\n");
PutDoc(">>");
writer.endObj(patObject);
Patterns.insert("Pattern"+Pdf::toPdf(patObject), patObject);
PdfId formObject = writer.newObject();
writer.startObj(formObject);
PutDoc("<<\n/Type /XObject\n/Subtype /Form\n");
PutDoc("/FormType 1\n");
PutDoc("/Group << /S /Transparency /CS /DeviceGray >>\n");
double lw = c->lineWidth();
PutDoc("/BBox ["+FToStr(-lw / 2.0)+" "+FToStr(lw / 2.0)+" "+FToStr(c->width()+lw)+" "+FToStr(-(c->height()+lw))+" ]\n");
PutDoc("/Resources << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]\n");
if (Patterns.count() != 0)
{
PutDoc("/Pattern << \n");
QMap<QString,int>::Iterator it3p;
for (it3p = Patterns.begin(); it3p != Patterns.end(); ++it3p)
PutDoc("/"+it3p.key()+" "+QString::number(it3p.value())+" 0 R\n");
PutDoc(">>\n");
}
PutDoc(">>\n");
QString stre = "q\n"+SetClipPath(c)+"h\n";
PutDoc("/Resources ");
Pdf::ResourceDictionary dict;
dict.Pattern = Patterns;
writer.write(dict);
 
QByteArray stre = "q\n"+SetClipPath(c)+"h\n";
stre += FToStr(fabs(c->lineWidth()))+" w\n";
stre += "/Pattern cs\n";
stre += "/Pattern"+QString::number(patObject)+" scn\nf*\n";
stre += "/Pattern"+Pdf::toPdf(patObject)+" scn\nf*\n";
stre += "Q\n";
if (Options.Compress)
stre = CompressStr(&stre);
PutDoc("/Length "+QString::number(stre.length())+"\n");
stre = CompressArray(stre);
PutDoc("/Length "+Pdf::toPdf(stre.length())+"\n");
if (Options.Compress)
PutDoc("/Filter /FlateDecode\n");
PutDoc(">>\nstream\n"+EncStream(stre, formObject)+"\nendstream\nendobj\n");
Seite.XObjects[ResNam+QString::number(ResCount)] = formObject;
PutDoc(">>\nstream\n"+EncStream(stre, formObject)+"\nendstream");
writer.endObj(shadeObjectT);
pageData.XObjects[ResNam+Pdf::toPdf(ResCount)] = formObject;
ResCount++;
QString GXName = ResNam+QString::number(ResCount);
QByteArray GXName = ResNam+Pdf::toPdf(ResCount);
ResCount++;
Transpar[GXName] = writeGState("/SMask << /S /Luminosity /G "+QString::number(formObject)+" 0 R >>\n/BM /Normal\n");
Transpar[GXName] = writeGState("/SMask << /S /Luminosity /G "+Pdf::toPdf(formObject)+" 0 R >>\n/BM /Normal\n");
TRes = GXName;
}
QString entx = "";
uint spotObject = 0;
uint shadeObject = newObject();
StartObj(shadeObject);
QByteArray entx = "";
PdfId spotObject = 0;
PdfId shadeObject = writer.newObject();
writer.startObj(shadeObject);
PutDoc("<<\n");
PutDoc("/ShadingType 7\n");
if (Options.UseRGB)
7148,16 → 7130,17
entx = "0 1 0 1 0 1 0 1";
if ((Options.UseSpotColors) && ((spotColorSet.count() > 0) && (spotColorSet.count() < 28)))
{
spotObject = newObject();
spotObject = writer.newObject();
PutDoc("/ColorSpace [ /DeviceN [ /Cyan /Magenta /Yellow /Black");
for (int sc = 0; sc < spotColorSet.count(); sc++)
{
PutDoc(" /"+spotColorSet.at(sc).simplified().replace("#", "#23").replace( QRegExp("[\\s\\/\\{\\[\\]\\}\\<\\>\\(\\)\\%]"), "#20" ));
PutDoc(" " + Pdf::toName(spotColorSet.at(sc).simplified()));
// " /"+spotColorSet.at(sc).simplified().replace("#", "#23").replace( QRegExp("[\\s\\/\\{\\[\\]\\}\\<\\>\\(\\)\\%]"), "#20" ));
entx += " 0 1";
}
PutDoc(" ]\n");
PutDoc("/DeviceCMYK\n");
PutDoc(QString::number(spotObject)+" 0 R\n");
PutDoc(Pdf::toPdf(spotObject)+" 0 R\n");
PutDoc("]\n");
spotMode = true;
}
7199,19 → 7182,21
}
}
PutDoc("/Decode [-40000 40000 -40000 40000 "+entx+"]\n");
QString dat = "";
QByteArray dat = "";
for (int vd = 0; vd < vertStream.count(); vd++)
{
dat += vertStream[vd];
}
if (Options.Compress)
dat = CompressStr(&dat);
PutDoc("/Length "+QString::number(dat.length())+"\n");
dat = CompressArray(dat);
PutDoc("/Length "+Pdf::toPdf(dat.length())+"\n");
if (Options.Compress)
PutDoc("/Filter /FlateDecode\n");
PutDoc(">>\nstream\n"+EncStream(dat, shadeObject)+"\nendstream\nendobj\n");
uint patObject = newObject();
StartObj(patObject);
PutDoc(">>\nstream\n"+EncStream(dat, shadeObject)+"\nendstream");
writer.endObj(shadeObject);
PdfId patObject = writer.newObject();
writer.startObj(patObject);
PutDoc("<<\n/Type /Pattern\n");
PutDoc("/PatternType 2\n");
QTransform mpa;
7234,13 → 7219,14
mpa.translate(patternStackPos.top().x(), patternStackPos.top().y());
}
PutDoc("/Matrix ["+FToStr(mpa.m11())+" "+FToStr(mpa.m12())+" "+FToStr(mpa.m21())+" "+FToStr(mpa.m22())+" "+FToStr(mpa.dx())+" "+FToStr(mpa.dy())+"]\n");
PutDoc("/Shading "+QString::number(shadeObject)+" 0 R\n");
PutDoc(">>\nendobj\n");
Patterns.insert("Pattern"+QString::number(patObject), patObject);
PutDoc("/Shading "+Pdf::toPdf(shadeObject)+" 0 R\n");
PutDoc(">>");
writer.endObj(patObject);
Patterns.insert("Pattern"+Pdf::toPdf(patObject), patObject);
if (spotMode)
{
QString colorDesc;
StartObj(spotObject);
QByteArray colorDesc;
writer.startObj(spotObject);
PutDoc("<<\n/FunctionType 4\n");
PutDoc("/Domain [0 1 0 1 0 1 0 1");
for (int sc = 0; sc < spotColorSet.count(); sc++)
7262,7 → 7248,7
if (sc == 0)
colorDesc += "dup "+FToStr(static_cast<double>(cc) / 255.0)+" mul ";
else
colorDesc += QString::number(sc*4 + 1)+" -1 roll dup "+FToStr(static_cast<double>(cc) / 255.0)+" mul ";
colorDesc += Pdf::toPdf(sc*4 + 1)+" -1 roll dup "+FToStr(static_cast<double>(cc) / 255.0)+" mul ";
colorDesc += "exch dup "+FToStr(static_cast<double>(mc) / 255.0)+" mul ";
colorDesc += "exch dup "+FToStr(static_cast<double>(yc) / 255.0)+" mul ";
colorDesc += "exch "+FToStr(static_cast<double>(kc) / 255.0)+" mul\n";
7273,14 → 7259,15
}
colorDesc += "}\n";
PutDoc("/Range [0 1 0 1 0 1 0 1]\n");
PutDoc("/Length "+QString::number(colorDesc.length()+1)+"\n");
PutDoc(">>\nstream\n"+EncStream(colorDesc, spotObject)+"\nendstream\nendobj\n");
PutDoc("/Length "+Pdf::toPdf(colorDesc.length()+1)+"\n");
PutDoc(">>\nstream\n"+EncStream(colorDesc, spotObject)+"\nendstream");
writer.endObj(spotObject);
}
QString tmp;
QByteArray tmp;
if (((Options.Version >= PDFOptions::PDFVersion_14) || (Options.Version == PDFOptions::PDFVersion_X4)) && (transparencyFound))
tmp += "/"+TRes+" gs\n";
tmp += Pdf::toName(TRes) + " gs\n";
tmp += "/Pattern cs\n";
tmp += "/Pattern"+QString::number(patObject)+" scn\n";
tmp += "/Pattern"+Pdf::toPdf(patObject)+" scn\n";
output = tmp;
if (tmpAddedColors.count() != 0)
{
7292,7 → 7279,7
return true;
}
 
bool PDFLibCore::PDF_PatchMeshGradientFill(QString& output, PageItem *c)
bool PDFLibCore::PDF_PatchMeshGradientFill(QByteArray& output, PageItem *c)
{
QList<double> StopVec;
QList<double> TransVec;
7359,11 → 7346,11
}
Gcolors.append(SetGradientColor(mp4.colorName, mp4.shade));
}
QString TRes("");
QByteArray TRes("");
if (((Options.Version >= PDFOptions::PDFVersion_14) || (Options.Version == PDFOptions::PDFVersion_X4)) && (transparencyFound))
{
uint shadeObjectT = newObject();
StartObj(shadeObjectT);
PdfId shadeObjectT = writer.newObject();
writer.startObj(shadeObjectT);
PutDoc("<<\n");
PutDoc("/ShadingType 7\n");
PutDoc("/ColorSpace /DeviceGray\n");
7397,63 → 7384,62
vst << encode16dVal(TransVec[colInd4]) << encode16dVal(TransVec[colInd1]) << encode16dVal(TransVec[colInd2]) << encode16dVal(TransVec[colInd3]);
}
PutDoc("/Decode [-40000 40000 -40000 40000 0 1]\n");
QString dat = "";
QByteArray dat = "";
for (int vd = 0; vd < vertStreamT.count(); vd++)
{
dat += vertStreamT[vd];
}
if (Options.Compress)
dat = CompressStr(&dat);
PutDoc("/Length "+QString::number(dat.length())+"\n");
dat = CompressArray(dat);
PutDoc("/Length "+Pdf::toPdf(dat.length())+"\n");
if (Options.Compress)
PutDoc("/Filter /FlateDecode\n");
PutDoc(">>\nstream\n"+EncStream(dat, shadeObjectT)+"\nendstream\nendobj\n");
uint patObject = newObject();
StartObj(patObject);
PutDoc(">>\nstream\n"+EncStream(dat, shadeObjectT)+"\nendstream");
writer.endObj(shadeObjectT);
PdfId patObject = writer.newObject();
writer.startObj(patObject);
PutDoc("<<\n/Type /Pattern\n");
PutDoc("/PatternType 2\n");
PutDoc("/Shading "+QString::number(shadeObjectT)+" 0 R\n");
PutDoc(">>\nendobj\n");
Patterns.insert("Pattern"+QString::number(patObject), patObject);
uint formObject = newObject();
StartObj(formObject);
PutDoc("/Shading "+Pdf::toPdf(shadeObjectT)+" 0 R\n");
PutDoc(">>");
writer.endObj(patObject);
Patterns.insert("Pattern"+Pdf::toPdf(patObject), patObject);
PdfId formObject = writer.newObject();
writer.startObj(formObject);
PutDoc("<<\n/Type /XObject\n/Subtype /Form\n");
PutDoc("/FormType 1\n");
PutDoc("/Group << /S /Transparency /CS /DeviceGray >>\n");
double lw = c->lineWidth();
PutDoc("/BBox ["+FToStr(-lw / 2.0)+" "+FToStr(lw / 2.0)+" "+FToStr(c->width()+lw)+" "+FToStr(-(c->height()+lw))+" ]\n");
PutDoc("/Resources << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]\n");
if (Patterns.count() != 0)
{
PutDoc("/Pattern << \n");
QMap<QString,int>::Iterator it3p;
for (it3p = Patterns.begin(); it3p != Patterns.end(); ++it3p)
PutDoc("/"+it3p.key()+" "+QString::number(it3p.value())+" 0 R\n");
PutDoc(">>\n");
}
PutDoc(">>\n");
QString stre = "q\n"+SetClipPath(c)+"h\n";
PutDoc("/Resources ");
Pdf::ResourceDictionary dict;
dict.Pattern = Patterns;
writer.write(dict);
QByteArray stre = "q\n"+SetClipPath(c)+"h\n";
stre += FToStr(fabs(c->lineWidth()))+" w\n";
stre += "/Pattern cs\n";
stre += "/Pattern"+QString::number(patObject)+" scn\nf*\n";
stre += "/Pattern"+Pdf::toPdf(patObject)+" scn\nf*\n";
stre += "Q\n";
if (Options.Compress)
stre = CompressStr(&stre);
PutDoc("/Length "+QString::number(stre.length())+"\n");
stre = CompressArray(stre);
PutDoc("/Length "+Pdf::toPdf(stre.length())+"\n");
if (Options.Compress)
PutDoc("/Filter /FlateDecode\n");
PutDoc(">>\nstream\n"+EncStream(stre, formObject)+"\nendstream\nendobj\n");
Seite.XObjects[ResNam+QString::number(ResCount)] = formObject;
PutDoc(">>\nstream\n"+EncStream(stre, formObject)+"\nendstream");
writer.endObj(formObject);
pageData.XObjects[ResNam+Pdf::toPdf(ResCount)] = formObject;
ResCount++;
QString GXName = ResNam+QString::number(ResCount);
QByteArray GXName = ResNam+Pdf::toPdf(ResCount);
ResCount++;
Transpar[GXName] = writeGState("/SMask << /S /Luminosity /G "+QString::number(formObject)+" 0 R >>\n/BM /Normal\n");
Transpar[GXName] = writeGState("/SMask << /S /Luminosity /G "+Pdf::toPdf(formObject)+" 0 R >>\n/BM /Normal\n");
TRes = GXName;
}
QString entx = "";
uint spotObject = 0;
uint shadeObject = newObject();
StartObj(shadeObject);
QByteArray entx = "";
PdfId spotObject = 0;
PdfId shadeObject = writer.newObject();
writer.startObj(shadeObject);
PutDoc("<<\n");
PutDoc("/ShadingType 7\n");
if (Options.UseRGB)
7476,16 → 7462,17
entx = "0 1 0 1 0 1 0 1";
if ((Options.UseSpotColors) && ((spotColorSet.count() > 0) && (spotColorSet.count() < 28)))
{
spotObject = newObject();
spotObject = writer.newObject();
PutDoc("/ColorSpace [ /DeviceN [ /Cyan /Magenta /Yellow /Black");
for (int sc = 0; sc < spotColorSet.count(); sc++)
{
PutDoc(" /"+spotColorSet.at(sc).simplified().replace("#", "#23").replace( QRegExp("[\\s\\/\\{\\[\\]\\}\\<\\>\\(\\)\\%]"), "#20" ));
PutDoc(" " + Pdf::toName(spotColorSet.at(sc).simplified()));
//" /"+spotColorSet.at(sc).simplified().replace("#", "#23").replace( QRegExp("[\\s\\/\\{\\[\\]\\}\\<\\>\\(\\)\\%]"), "#20" ));
entx += " 0 1";
}
PutDoc(" ]\n");
PutDoc("/DeviceCMYK\n");
PutDoc(QString::number(spotObject)+" 0 R\n");
PutDoc(Pdf::toObjRef(spotObject) + "\n");
PutDoc("]\n");
spotMode = true;
}
7525,19 → 7512,20
encodeColor(vs, colorNames[colInd3], colorShades[colInd3], spotColorSet, spotMode);
}
PutDoc("/Decode [-40000 40000 -40000 40000 "+entx+"]\n");
QString dat = "";
QByteArray dat = "";
for (int vd = 0; vd < vertStream.count(); vd++)
{
dat += vertStream[vd];
}
if (Options.Compress)
dat = CompressStr(&dat);
PutDoc("/Length "+QString::number(dat.length())+"\n");
dat = CompressArray(dat);
PutDoc("/Length "+Pdf::toPdf(dat.length())+"\n");
if (Options.Compress)
PutDoc("/Filter /FlateDecode\n");
PutDoc(">>\nstream\n"+EncStream(dat, shadeObject)+"\nendstream\nendobj\n");
uint patObject = newObject();
StartObj(patObject);
PutDoc(">>\nstream\n"+EncStream(dat, shadeObject)+"\nendstream");
writer.endObj(shadeObject);
PdfId patObject = writer.newObject();
writer.startObj(patObject);
PutDoc("<<\n/Type /Pattern\n");
PutDoc("/PatternType 2\n");
QTransform mpa;
7560,13 → 7548,14
mpa.translate(patternStackPos.top().x(), patternStackPos.top().y());
}
PutDoc("/Matrix ["+FToStr(mpa.m11())+" "+FToStr(mpa.m12())+" "+FToStr(mpa.m21())+" "+FToStr(mpa.m22())+" "+FToStr(mpa.dx())+" "+FToStr(mpa.dy())+"]\n");
PutDoc("/Shading "+QString::number(shadeObject)+" 0 R\n");
PutDoc(">>\nendobj\n");
Patterns.insert("Pattern"+QString::number(patObject), patObject);
PutDoc("/Shading "+Pdf::toPdf(shadeObject)+" 0 R\n");
PutDoc(">>");
writer.endObj(patObject);
Patterns.insert("Pattern"+Pdf::toPdf(patObject), patObject);
if (spotMode)
{
QString colorDesc;
StartObj(spotObject);
QByteArray colorDesc;
writer.startObj(spotObject);
PutDoc("<<\n/FunctionType 4\n");
PutDoc("/Domain [0 1 0 1 0 1 0 1");
for (int sc = 0; sc < spotColorSet.count(); sc++)
7588,7 → 7577,7
if (sc == 0)
colorDesc += "dup "+FToStr(static_cast<double>(cc) / 255.0)+" mul ";
else
colorDesc += QString::number(sc*4 + 1)+" -1 roll dup "+FToStr(static_cast<double>(cc) / 255.0)+" mul ";
colorDesc += Pdf::toPdf(sc*4 + 1)+" -1 roll dup "+FToStr(static_cast<double>(cc) / 255.0)+" mul ";
colorDesc += "exch dup "+FToStr(static_cast<double>(mc) / 255.0)+" mul ";
colorDesc += "exch dup "+FToStr(static_cast<double>(yc) / 255.0)+" mul ";
colorDesc += "exch "+FToStr(static_cast<double>(kc) / 255.0)+" mul\n";
7599,19 → 7588,20
}
colorDesc += "}\n";
PutDoc("/Range [0 1 0 1 0 1 0 1]\n");
PutDoc("/Length "+QString::number(colorDesc.length()+1)+"\n");
PutDoc(">>\nstream\n"+EncStream(colorDesc, spotObject)+"\nendstream\nendobj\n");
PutDoc("/Length "+Pdf::toPdf(colorDesc.length()+1)+"\n");
PutDoc(">>\nstream\n"+EncStream(colorDesc, spotObject)+"\nendstream");
writer.endObj(spotObject);
}
QString tmp;
QByteArray tmp;
if (((Options.Version >= PDFOptions::PDFVersion_14) || (Options.Version == PDFOptions::PDFVersion_X4)) && (transparencyFound))
tmp += "/"+TRes+" gs\n";
tmp += Pdf::toName(TRes) + " gs\n";
tmp += "/Pattern cs\n";
tmp += "/Pattern"+QString::number(patObject)+" scn\n";
tmp += "/Pattern"+Pdf::toPdf(patObject)+" scn\n";
output = tmp;
return true;
}
 
bool PDFLibCore::PDF_DiamondGradientFill(QString& output, PageItem *c)
bool PDFLibCore::PDF_DiamondGradientFill(QByteArray& output, PageItem *c)
{
QList<double> StopVec;
QList<double> TransVec;
7653,11 → 7643,11
QLineF edge2 = QLineF(cP, QPointF(c->GrControl2.x(), -c->GrControl2.y()));
QLineF edge3 = QLineF(cP, QPointF(c->GrControl3.x(), -c->GrControl3.y()));
QLineF edge4 = QLineF(cP, QPointF(c->GrControl4.x(), -c->GrControl4.y()));
QString TRes("");
QByteArray TRes("");
if (((Options.Version >= PDFOptions::PDFVersion_14) || (Options.Version == PDFOptions::PDFVersion_X4)) && (transparencyFound))
{
uint shadeObjectT = newObject();
StartObj(shadeObjectT);
PdfId shadeObjectT = writer.newObject();
writer.startObj(shadeObjectT);
PutDoc("<<\n");
PutDoc("/ShadingType 6\n");
PutDoc("/ColorSpace /DeviceGray\n");
7743,63 → 7733,62
}
}
PutDoc("/Decode [-40000 40000 -40000 40000 0 1]\n");
QString dat = "";
QByteArray dat = "";
for (int vd = 0; vd < vertStreamT.count(); vd++)
{
dat += vertStreamT[vd];
}
if (Options.Compress)
dat = CompressStr(&dat);
PutDoc("/Length "+QString::number(dat.length())+"\n");
dat = CompressArray(dat);
PutDoc("/Length "+Pdf::toPdf(dat.length())+"\n");
if (Options.Compress)
PutDoc("/Filter /FlateDecode\n");
PutDoc(">>\nstream\n"+EncStream(dat, shadeObjectT)+"\nendstream\nendobj\n");
uint patObject = newObject();
StartObj(patObject);
PutDoc(">>\nstream\n"+EncStream(dat, shadeObjectT)+"\nendstream");
writer.endObj(shadeObjectT);
PdfId patObject = writer.newObject();
writer.startObj(patObject);
PutDoc("<<\n/Type /Pattern\n");
PutDoc("/PatternType 2\n");
PutDoc("/Shading "+QString::number(shadeObjectT)+" 0 R\n");
PutDoc(">>\nendobj\n");
Patterns.insert("Pattern"+QString::number(patObject), patObject);
uint formObject = newObject();
StartObj(formObject);
PutDoc("/Shading "+Pdf::toPdf(shadeObjectT)+" 0 R\n");
PutDoc(">>");
writer.endObj(patObject);
Patterns.insert("Pattern"+Pdf::toPdf(patObject), patObject);
PdfId formObject = writer.newObject();
writer.startObj(formObject);
PutDoc("<<\n/Type /XObject\n/Subtype /Form\n");
PutDoc("/FormType 1\n");
PutDoc("/Group << /S /Transparency /CS /DeviceGray >>\n");
double lw = c->lineWidth();
PutDoc("/BBox ["+FToStr(-lw / 2.0)+" "+FToStr(lw / 2.0)+" "+FToStr(c->width()+lw)+" "+FToStr(-(c->height()+lw))+" ]\n");
PutDoc("/Resources << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]\n");
if (Patterns.count() != 0)
{
PutDoc("/Pattern << \n");
QMap<QString,int>::Iterator it3p;
for (it3p = Patterns.begin(); it3p != Patterns.end(); ++it3p)
PutDoc("/"+it3p.key()+" "+QString::number(it3p.value())+" 0 R\n");
PutDoc(">>\n");
}
PutDoc(">>\n");
QString stre = "q\n"+SetClipPath(c)+"h\n";
PutDoc("/Resources ");
Pdf::ResourceDictionary dict;
dict.Pattern = Patterns;
writer.write(dict);
QByteArray stre = "q\n"+SetClipPath(c)+"h\n";
stre += FToStr(fabs(c->lineWidth()))+" w\n";
stre += "/Pattern cs\n";
stre += "/Pattern"+QString::number(patObject)+" scn\nf*\n";
stre += "/Pattern"+Pdf::toPdf(patObject)+" scn\nf*\n";
stre += "Q\n";
if (Options.Compress)
stre = CompressStr(&stre);
PutDoc("/Length "+QString::number(stre.length())+"\n");
stre = CompressArray(stre);
PutDoc("/Length "+Pdf::toPdf(stre.length())+"\n");
if (Options.Compress)
PutDoc("/Filter /FlateDecode\n");
PutDoc(">>\nstream\n"+EncStream(stre, formObject)+"\nendstream\nendobj\n");
Seite.XObjects[ResNam+QString::number(ResCount)] = formObject;
PutDoc(">>\nstream\n"+EncStream(stre, formObject)+"\nendstream");
writer.endObj(formObject);
pageData.XObjects[ResNam+Pdf::toPdf(ResCount)] = formObject;
ResCount++;
QString GXName = ResNam+QString::number(ResCount);
QByteArray GXName = ResNam+Pdf::toPdf(ResCount);
ResCount++;
Transpar[GXName] = writeGState("/SMask << /S /Luminosity /G "+QString::number(formObject)+" 0 R >>\n/BM /Normal\n");
Transpar[GXName] = writeGState("/SMask << /S /Luminosity /G "+Pdf::toPdf(formObject)+" 0 R >>\n/BM /Normal\n");
TRes = GXName;
}
QString entx = "";
uint spotObject = 0;
uint shadeObject = newObject();
StartObj(shadeObject);
QByteArray entx = "";
PdfId spotObject = 0;
PdfId shadeObject = writer.newObject();
writer.startObj(shadeObject);
PutDoc("<<\n");
PutDoc("/ShadingType 6\n");
if (Options.UseRGB)
7822,16 → 7811,17
entx = "0 1 0 1 0 1 0 1";
if ((Options.UseSpotColors) && ((spotColorSet.count() > 0) && (spotColorSet.count() < 28)))
{
spotObject = newObject();
spotObject = writer.newObject();
PutDoc("/ColorSpace [ /DeviceN [ /Cyan /Magenta /Yellow /Black");
for (int sc = 0; sc < spotColorSet.count(); sc++)
{
PutDoc(" /"+spotColorSet.at(sc).simplified().replace("#", "#23").replace( QRegExp("[\\s\\/\\{\\[\\]\\}\\<\\>\\(\\)\\%]"), "#20" ));
PutDoc(" " + Pdf::toName(spotColorSet.at(sc).simplified()));
//" /"+spotColorSet.at(sc).simplified().replace("#", "#23").replace( QRegExp("[\\s\\/\\{\\[\\]\\}\\<\\>\\(\\)\\%]"), "#20" ));
entx += " 0 1";
}
PutDoc(" ]\n");
PutDoc("/DeviceCMYK\n");
PutDoc(QString::number(spotObject)+" 0 R\n");
PutDoc(Pdf::toPdf(spotObject)+" 0 R\n");
PutDoc("]\n");
spotMode = true;
}
7838,7 → 7828,7
else
PutDoc("/ColorSpace /DeviceCMYK\n");
}
PutDoc("/Background [" + Gcolors.last() + "]\n");
PutDoc("/Background [" + Pdf::toAscii(Gcolors.last()) + "]\n");
PutDoc("/BitsPerCoordinate 32\n");
PutDoc("/BitsPerComponent 16\n");
PutDoc("/BitsPerFlag 8\n");
7944,19 → 7934,20
}
}
PutDoc("/Decode [-40000 40000 -40000 40000 "+entx+"]\n");
QString dat = "";
QByteArray dat = "";
for (int vd = 0; vd < vertStream.count(); vd++)
{
dat += vertStream[vd];
}
if (Options.Compress)
dat = CompressStr(&dat);
PutDoc("/Length "+QString::number(dat.length())+"\n");
dat = CompressArray(dat);
PutDoc("/Length "+Pdf::toPdf(dat.length())+"\n");
if (Options.Compress)
PutDoc("/Filter /FlateDecode\n");
PutDoc(">>\nstream\n"+EncStream(dat, shadeObject)+"\nendstream\nendobj\n");
uint patObject = newObject();
StartObj(patObject);
PutDoc(">>\nstream\n"+EncStream(dat, shadeObject)+"\nendstream");
writer.endObj(shadeObject);
PdfId patObject = writer.newObject();
writer.startObj(patObject);
PutDoc("<<\n/Type /Pattern\n");
PutDoc("/PatternType 2\n");
QTransform mpa;
7979,13 → 7970,14
mpa.translate(patternStackPos.top().x(), patternStackPos.top().y());
}
PutDoc("/Matrix ["+FToStr(mpa.m11())+" "+FToStr(mpa.m12())+" "+FToStr(mpa.m21())+" "+FToStr(mpa.m22())+" "+FToStr(mpa.dx())+" "+FToStr(mpa.dy())+"]\n");
PutDoc("/Shading "+QString::number(shadeObject)+" 0 R\n");
PutDoc(">>\nendobj\n");
Patterns.insert("Pattern"+QString::number(patObject), patObject);
PutDoc("/Shading "+Pdf::toPdf(shadeObject)+" 0 R\n");
PutDoc(">>");
writer.endObj(patObject);
Patterns.insert("Pattern"+Pdf::toPdf(patObject), patObject);
if (spotMode)
{
QString colorDesc;
StartObj(spotObject);
QByteArray colorDesc;
writer.startObj(spotObject);
PutDoc("<<\n/FunctionType 4\n");
PutDoc("/Domain [0 1 0 1 0 1 0 1");
for (int sc = 0; sc < spotColorSet.count(); sc++)
8007,7 → 7999,7
if (sc == 0)
colorDesc += "dup "+FToStr(static_cast<double>(cc) / 255.0)+" mul ";
else
colorDesc += QString::number(sc*4 + 1)+" -1 roll dup "+FToStr(static_cast<double>(cc) / 255.0)+" mul ";
colorDesc += Pdf::toPdf(sc*4 + 1)+" -1 roll dup "+FToStr(static_cast<double>(cc) / 255.0)+" mul ";
colorDesc += "exch dup "+FToStr(static_cast<double>(mc) / 255.0)+" mul ";
colorDesc += "exch dup "+FToStr(static_cast<double>(yc) / 255.0)+" mul ";
colorDesc += "exch "+FToStr(static_cast<double>(kc) / 255.0)+" mul\n";
8018,20 → 8010,21
}
colorDesc += "}\n";
PutDoc("/Range [0 1 0 1 0 1 0 1]\n");
PutDoc("/Length "+QString::number(colorDesc.length()+1)+"\n");
PutDoc(">>\nstream\n"+EncStream(colorDesc, spotObject)+"\nendstream\nendobj\n");
PutDoc("/Length "+Pdf::toPdf(colorDesc.length()+1)+"\n");
PutDoc(">>\nstream\n"+EncStream(colorDesc, spotObject)+"\nendstream");
writer.endObj(spotObject);
}
QString tmp;
QByteArray tmp;
if (((Options.Version >= PDFOptions::PDFVersion_14) || (Options.Version == PDFOptions::PDFVersion_X4)) && (transparencyFound))
tmp += "/"+TRes+" gs\n";
tmp += Pdf::toName(TRes) + " gs\n";
tmp += "/Pattern cs\n";
tmp += "/Pattern"+QString::number(patObject)+" scn\n";
tmp += "/Pattern"+Pdf::toPdf(patObject)+" scn\n";
output = tmp;
return true;
 
}
 
bool PDFLibCore::PDF_TensorGradientFill(QString& output, PageItem *c)
bool PDFLibCore::PDF_TensorGradientFill(QByteArray& output, PageItem *c)
{
QList<int> colorShades;
QStringList spotColorSet;
8070,11 → 8063,11
}
Gcolors.append(SetGradientColor(colorNames.at(cst), colorShades[cst]));
}
QString TRes("");
QByteArray TRes("");
if (((Options.Version >= PDFOptions::PDFVersion_14) || (Options.Version == PDFOptions::PDFVersion_X4)) && (transparencyFound))
{
uint shadeObjectT = newObject();
StartObj(shadeObjectT);
PdfId shadeObjectT = writer.newObject();
writer.startObj(shadeObjectT);
PutDoc("<<\n");
PutDoc("/ShadingType 7\n");
PutDoc("/ColorSpace /DeviceGray\n");
8105,63 → 8098,62
vst << encode32dVal(c->GrControl2.x()) << encode32dVal(-c->GrControl2.y());
vst << encode16dVal(c->GrCol4transp) << encode16dVal(c->GrCol1transp) << encode16dVal(c->GrCol2transp) << encode16dVal(c->GrCol3transp);
PutDoc("/Decode [-40000 40000 -40000 40000 0 1]\n");
QString dat = "";
QByteArray dat = "";
for (int vd = 0; vd < vertStreamT.count(); vd++)
{
dat += vertStreamT[vd];
}
if (Options.Compress)
dat = CompressStr(&dat);
PutDoc("/Length "+QString::number(dat.length())+"\n");
dat = CompressArray(dat);
PutDoc("/Length "+Pdf::toPdf(dat.length())+"\n");
if (Options.Compress)
PutDoc("/Filter /FlateDecode\n");
PutDoc(">>\nstream\n"+EncStream(dat, shadeObjectT)+"\nendstream\nendobj\n");
uint patObject = newObject();
StartObj(patObject);
PutDoc(">>\nstream\n"+EncStream(dat, shadeObjectT)+"\nendstream");
writer.endObj(shadeObjectT);
PdfId patObject = writer.newObject();
writer.startObj(patObject);
PutDoc("<<\n/Type /Pattern\n");
PutDoc("/PatternType 2\n");
PutDoc("/Shading "+QString::number(shadeObjectT)+" 0 R\n");
PutDoc(">>\nendobj\n");
Patterns.insert("Pattern"+QString::number(patObject), patObject);
uint formObject = newObject();
StartObj(formObject);
PutDoc("/Shading "+Pdf::toPdf(shadeObjectT)+" 0 R\n");
PutDoc(">>");
writer.endObj(patObject);
Patterns.insert("Pattern"+Pdf::toPdf(patObject), patObject);
PdfId formObject = writer.newObject();
writer.startObj(formObject);
PutDoc("<<\n/Type /XObject\n/Subtype /Form\n");
PutDoc("/FormType 1\n");
PutDoc("/Group << /S /Transparency /CS /DeviceGray >>\n");
double lw = c->lineWidth();
PutDoc("/BBox ["+FToStr(-lw / 2.0)+" "+FToStr(lw / 2.0)+" "+FToStr(c->width()+lw)+" "+FToStr(-(c->height()+lw))+" ]\n");
PutDoc("/Resources << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]\n");
if (Patterns.count() != 0)
{
PutDoc("/Pattern << \n");
QMap<QString,int>::Iterator it3p;
for (it3p = Patterns.begin(); it3p != Patterns.end(); ++it3p)
PutDoc("/"+it3p.key()+" "+QString::number(it3p.value())+" 0 R\n");
PutDoc(">>\n");
}
PutDoc(">>\n");
QString stre = "q\n"+SetClipPath(c)+"h\n";
PutDoc("/Resources ");
Pdf::ResourceDictionary dict;
dict.Pattern = Patterns;
writer.write(dict);
QByteArray stre = "q\n"+SetClipPath(c)+"h\n";
stre += FToStr(fabs(c->lineWidth()))+" w\n";
stre += "/Pattern cs\n";
stre += "/Pattern"+QString::number(patObject)+" scn\nf*\n";
stre += "/Pattern"+Pdf::toPdf(patObject)+" scn\nf*\n";
stre += "Q\n";
if (Options.Compress)
stre = CompressStr(&stre);
PutDoc("/Length "+QString::number(stre.length())+"\n");
stre = CompressArray(stre);
PutDoc("/Length "+Pdf::toPdf(stre.length())+"\n");
if (Options.Compress)
PutDoc("/Filter /FlateDecode\n");
PutDoc(">>\nstream\n"+EncStream(stre, formObject)+"\nendstream\nendobj\n");
Seite.XObjects[ResNam+QString::number(ResCount)] = formObject;
PutDoc(">>\nstream\n"+EncStream(stre, formObject)+"\nendstream");
writer.endObj(formObject);
pageData.XObjects[ResNam+Pdf::toPdf(ResCount)] = formObject;
ResCount++;
QString GXName = ResNam+QString::number(ResCount);
QByteArray GXName = ResNam+Pdf::toPdf(ResCount);
ResCount++;
Transpar[GXName] = writeGState("/SMask << /S /Luminosity /G "+QString::number(formObject)+" 0 R >>\n/BM /Normal\n");
Transpar[GXName] = writeGState("/SMask << /S /Luminosity /G "+Pdf::toPdf(formObject)+" 0 R >>\n/BM /Normal\n");
TRes = GXName;
}
QString entx = "";
uint spotObject = 0;
uint shadeObject = newObject();
StartObj(shadeObject);
QByteArray entx = "";
PdfId spotObject = 0;
PdfId shadeObject = writer.newObject();
writer.startObj(shadeObject);
PutDoc("<<\n");
PutDoc("/ShadingType 7\n");
if (Options.UseRGB)
8184,16 → 8176,17
entx = "0 1 0 1 0 1 0 1";
if ((Options.UseSpotColors) && ((spotColorSet.count() > 0) && (spotColorSet.count() < 28)))
{
spotObject = newObject();
spotObject = writer.newObject();
PutDoc("/ColorSpace [ /DeviceN [ /Cyan /Magenta /Yellow /Black");
for (int sc = 0; sc < spotColorSet.count(); sc++)
{
PutDoc(" /"+spotColorSet.at(sc).simplified().replace("#", "#23").replace( QRegExp("[\\s\\/\\{\\[\\]\\}\\<\\>\\(\\)\\%]"), "#20" ));
PutDoc(" " + Pdf::toName(spotColorSet.at(sc).simplified()));
//" /"+spotColorSet.at(sc).simplified().replace("#", "#23").replace( QRegExp("[\\s\\/\\{\\[\\]\\}\\<\\>\\(\\)\\%]"), "#20" ));
entx += " 0 1";
}
PutDoc(" ]\n");
PutDoc("/DeviceCMYK\n");
PutDoc(QString::number(spotObject)+" 0 R\n");
PutDoc(Pdf::toPdf(spotObject)+" 0 R\n");
PutDoc("]\n");
spotMode = true;
}
8263,19 → 8256,20
}
}
PutDoc("/Decode [-40000 40000 -40000 40000 "+entx+"]\n");
QString dat = "";
QByteArray dat = "";
for (int vd = 0; vd < vertStream.count(); vd++)
{
dat += vertStream[vd];
}
if (Options.Compress)
dat = CompressStr(&dat);
PutDoc("/Length "+QString::number(dat.length())+"\n");
dat = CompressArray(dat);
PutDoc("/Length "+Pdf::toPdf(dat.length())+"\n");
if (Options.Compress)
PutDoc("/Filter /FlateDecode\n");
PutDoc(">>\nstream\n"+EncStream(dat, shadeObject)+"\nendstream\nendobj\n");
uint patObject = newObject();
StartObj(patObject);
PutDoc(">>\nstream\n"+EncStream(dat, shadeObject)+"\nendstream");
writer.endObj(shadeObject);
PdfId patObject = writer.newObject();
writer.startObj(patObject);
PutDoc("<<\n/Type /Pattern\n");
PutDoc("/PatternType 2\n");
QTransform mpa;
8298,13 → 8292,14
mpa.translate(patternStackPos.top().x(), patternStackPos.top().y());
}
PutDoc("/Matrix ["+FToStr(mpa.m11())+" "+FToStr(mpa.m12())+" "+FToStr(mpa.m21())+" "+FToStr(mpa.m22())+" "+FToStr(mpa.dx())+" "+FToStr(mpa.dy())+"]\n");
PutDoc("/Shading "+QString::number(shadeObject)+" 0 R\n");
PutDoc(">>\nendobj\n");
Patterns.insert("Pattern"+QString::number(patObject), patObject);
PutDoc("/Shading "+Pdf::toPdf(shadeObject)+" 0 R\n");
PutDoc(">>");
writer.endObj(patObject);
Patterns.insert("Pattern"+Pdf::toPdf(patObject), patObject);
if (spotMode)
{
QString colorDesc;
StartObj(spotObject);
QByteArray colorDesc;
writer.startObj(spotObject);
PutDoc("<<\n/FunctionType 4\n");
PutDoc("/Domain [0 1 0 1 0 1 0 1");
for (int sc = 0; sc < spotColorSet.count(); sc++)
8326,7 → 8321,7
if (sc == 0)
colorDesc += "dup "+FToStr(static_cast<double>(cc) / 255.0)+" mul ";
else
colorDesc += QString::number(sc*4 + 1)+" -1 roll dup "+FToStr(static_cast<double>(cc) / 255.0)+" mul ";
colorDesc += Pdf::toPdf(sc*4 + 1)+" -1 roll dup "+FToStr(static_cast<double>(cc) / 255.0)+" mul ";
colorDesc += "exch dup "+FToStr(static_cast<double>(mc) / 255.0)+" mul ";
colorDesc += "exch dup "+FToStr(static_cast<double>(yc) / 255.0)+" mul ";
colorDesc += "exch "+FToStr(static_cast<double>(kc) / 255.0)+" mul\n";
8337,19 → 8332,20
}
colorDesc += "}\n";
PutDoc("/Range [0 1 0 1 0 1 0 1]\n");
PutDoc("/Length "+QString::number(colorDesc.length()+1)+"\n");
PutDoc(">>\nstream\n"+EncStream(colorDesc, spotObject)+"\nendstream\nendobj\n");
PutDoc("/Length "+Pdf::toPdf(colorDesc.length()+1)+"\n");
PutDoc(">>\nstream\n"+EncStream(colorDesc, spotObject)+"\nendstream");
writer.endObj(spotObject);
}
QString tmp;
QByteArray tmp;
if (((Options.Version >= PDFOptions::PDFVersion_14) || (Options.Version == PDFOptions::PDFVersion_X4)) && (transparencyFound))
tmp += "/"+TRes+" gs\n";
tmp += Pdf::toName(TRes) + " gs\n";
tmp += "/Pattern cs\n";
tmp += "/Pattern"+QString::number(patObject)+" scn\n";
tmp += "/Pattern"+Pdf::toPdf(patObject)+" scn\n";
output = tmp;
return true;
}
 
bool PDFLibCore::PDF_GradientFillStroke(QString& output, PageItem *currItem, bool stroke, bool forArrow)
bool PDFLibCore::PDF_GradientFillStroke(QByteArray& output, PageItem *currItem, bool stroke, bool forArrow)
{
QList<double> StopVec;
QList<double> TransVec;
8503,7 → 8499,7
}
lastStop = actualStop;
}
QString TRes("");
QByteArray TRes;
if (((Options.Version >= PDFOptions::PDFVersion_14) || (Options.Version == PDFOptions::PDFVersion_X4)) && (transparencyFound))
{
QTransform mpM;
8525,8 → 8521,8
mpM.translate(-StartX, StartY);
mpM.scale(1, Gscale);
}
uint patObject = newObject();
StartObj(patObject);
PdfId patObject = writer.newObject();
writer.startObj(patObject);
PutDoc("<<\n/Type /Pattern\n");
PutDoc("/PatternType 2\n");
PutDoc("/Matrix ["+FToStr(mpM.m11())+" "+FToStr(mpM.m12())+" "+FToStr(mpM.m21())+" "+FToStr(mpM.m22())+" "+FToStr(mpM.dx())+" "+FToStr(mpM.dy())+"]\n");
8552,7 → 8548,7
if (StopVec.count() > 2)
{
PutDoc("/Bounds [");
QString bctx = "";
QByteArray bctx = "";
for (int bc = 1; bc < StopVec.count() - 1; bc++)
{
bctx += FToStr(StopVec.at(bc))+" ";
8561,7 → 8557,7
}
else
PutDoc("/Bounds []\n");
QString entx = "";
QByteArray entx = "";
PutDoc("/Functions\n");
PutDoc("[\n");
for (int cc = 0; cc < TransVec.count() - 1; cc++)
8580,53 → 8576,51
PutDoc(">>\n");
PutDoc(">>\n");
PutDoc(">>\n");
Patterns.insert("Pattern"+QString::number(patObject), patObject);
uint formObject = newObject();
StartObj(formObject);
writer.endObj(patObject);
Patterns.insert("Pattern"+Pdf::toPdf(patObject), patObject);
PdfId formObject = writer.newObject();
writer.startObj(formObject);
PutDoc("<<\n/Type /XObject\n/Subtype /Form\n");
PutDoc("/FormType 1\n");
PutDoc("/Group << /S /Transparency /CS /DeviceGray >>\n");
double lw = currItem->lineWidth();
PutDoc("/BBox ["+FToStr(-lw / 2.0)+" "+FToStr(lw / 2.0)+" "+FToStr(currItem->width()+lw)+" "+FToStr(-(currItem->height()+lw))+" ]\n");
PutDoc("/Resources << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]\n");
if (Patterns.count() != 0)
{
PutDoc("/Pattern << \n");
QMap<QString,int>::Iterator it3p;
for (it3p = Patterns.begin(); it3p != Patterns.end(); ++it3p)
PutDoc("/"+it3p.key()+" "+QString::number(it3p.value())+" 0 R\n");
PutDoc(">>\n");
}
PutDoc(">>\n");
QString stre = "q\n"+SetClipPath(currItem)+"h\n";
PutDoc("/Resources ");
Pdf::ResourceDictionary dict;
dict.Pattern = Patterns;
writer.write(dict);
 
QByteArray stre = "q\n"+SetClipPath(currItem)+"h\n";
stre += FToStr(fabs(currItem->lineWidth()))+" w\n";
if ((forArrow) || (!stroke))
{
stre += "/Pattern cs\n";
stre += "/Pattern"+QString::number(patObject)+" scn\nf*\n";
stre += "/Pattern"+Pdf::toPdf(patObject)+" scn\nf*\n";
}
else
{
stre += "/Pattern CS\n";
stre += "/Pattern"+QString::number(patObject)+" SCN\nS\n";
stre += "/Pattern"+Pdf::toPdf(patObject)+" SCN\nS\n";
}
stre += "Q\n";
if (Options.Compress)
stre = CompressStr(&stre);
PutDoc("/Length "+QString::number(stre.length())+"\n");
stre = CompressArray(stre);
PutDoc("/Length "+Pdf::toPdf(stre.length())+"\n");
if (Options.Compress)
PutDoc("/Filter /FlateDecode\n");
PutDoc(">>\nstream\n"+EncStream(stre, formObject)+"\nendstream\nendobj\n");
Seite.XObjects[ResNam+QString::number(ResCount)] = formObject;
PutDoc(">>\nstream\n"+EncStream(stre, formObject)+"\nendstream");
writer.endObj(formObject);
pageData.XObjects[ResNam+Pdf::toPdf(ResCount)] = formObject;
ResCount++;
QString GXName = ResNam+QString::number(ResCount);
QByteArray GXName = ResNam+Pdf::toPdf(ResCount);
ResCount++;
Transpar[GXName] = writeGState("/SMask << /S /Luminosity /G "+QString::number(formObject)+" 0 R >>\n/BM /Normal\n");
Transpar[GXName] = writeGState("/SMask << /S /Luminosity /G "+Pdf::toPdf(formObject)+" 0 R >>\n/BM /Normal\n");
TRes = GXName;
}
uint patObject = newObject();
uint spotObject = 0;
StartObj(patObject);
PdfId patObject = writer.newObject();
PdfId spotObject = 0;
writer.startObj(patObject);
PutDoc("<<\n/Type /Pattern\n");
PutDoc("/PatternType 2\n");
PutDoc("/Matrix ["+FToStr(mpa.m11())+" "+FToStr(mpa.m12())+" "+FToStr(mpa.m21())+" "+FToStr(mpa.m22())+" "+FToStr(mpa.dx())+" "+FToStr(mpa.dy())+"]\n");
8646,15 → 8640,16
{
if ((Options.UseSpotColors) && ((spotColorSet.count() > 0) && (spotColorSet.count() < 28)))
{
spotObject = newObject();
spotObject = writer.newObject();
PutDoc("/ColorSpace [ /DeviceN [ /Cyan /Magenta /Yellow /Black");
for (int sc = 0; sc < spotColorSet.count(); sc++)
{
PutDoc(" /"+spotColorSet.at(sc).simplified().replace("#", "#23").replace( QRegExp("[\\s\\/\\{\\[\\]\\}\\<\\>\\(\\)\\%]"), "#20" ));
PutDoc("" + Pdf::toName(spotColorSet.at(sc).simplified()));
//" /"+spotColorSet.at(sc).simplified().replace("#", "#23").replace( QRegExp("[\\s\\/\\{\\[\\]\\}\\<\\>\\(\\)\\%]"), "#20" ));
}
PutDoc(" ]\n");
PutDoc("/DeviceCMYK\n");
PutDoc(QString::number(spotObject)+" 0 R\n");
PutDoc(Pdf::toPdf(spotObject)+" 0 R\n");
PutDoc("]\n");
spotMode = true;
}
8676,7 → 8671,7
if (StopVec.count() > 2)
{
PutDoc("/Bounds [");
QString bctx = "";
QByteArray bctx = "";
for (int bc = 1; bc < StopVec.count() - 1; bc++)
{
bctx += FToStr(StopVec.at(bc))+" ";
8685,7 → 8680,7
}
else
PutDoc("/Bounds []\n");
QString entx = "";
QByteArray entx = "";
PutDoc("/Functions\n");
PutDoc("[\n");
for (int cc = 0; cc < colorNames.count() - 1; cc++)
8709,7 → 8704,7
}
else
{
PutDoc("/C0 ["+Gcolors[cc]);
PutDoc("/C0 [" + Pdf::toAscii(Gcolors[cc]));
for (int sc = 0; sc < spotColorSet.count(); sc++)
{
PutDoc(" 0");
8729,7 → 8724,7
}
else
{
PutDoc("/C1 ["+Gcolors[cc+1]);
PutDoc("/C1 [" + Pdf::toAscii(Gcolors[cc+1]));
for (int sc = 0; sc < spotColorSet.count(); sc++)
{
PutDoc(" 0");
8739,8 → 8734,8
}
else
{
PutDoc("/C0 ["+Gcolors[cc]+"]\n");
PutDoc("/C1 ["+Gcolors[cc+1]+"]\n");
PutDoc("/C0 [" + Pdf::toAscii(Gcolors[cc]) + "]\n");
PutDoc("/C1 [" + Pdf::toAscii(Gcolors[cc+1]) + "]\n");
}
PutDoc("/N 1\n");
PutDoc(">>\n");
8750,12 → 8745,12
PutDoc(">>\n");
PutDoc(">>\n");
PutDoc(">>\n");
PutDoc("endobj\n");
Patterns.insert("Pattern"+QString::number(patObject), patObject);
writer.endObj(patObject);
Patterns.insert("Pattern"+Pdf::toPdf(patObject), patObject);
if (spotMode)
{
QString colorDesc;
StartObj(spotObject);
QByteArray colorDesc;
writer.startObj(spotObject);
PutDoc("<<\n/FunctionType 4\n");
PutDoc("/Domain [0 1 0 1 0 1 0 1");
for (int sc = 0; sc < spotColorSet.count(); sc++)
8777,7 → 8772,7
if (sc == 0)
colorDesc += "dup "+FToStr(static_cast<double>(cc) / 255.0)+" mul ";
else
colorDesc += QString::number(sc*4 + 1)+" -1 roll dup "+FToStr(static_cast<double>(cc) / 255.0)+" mul ";
colorDesc += Pdf::toPdf(sc*4 + 1)+" -1 roll dup "+FToStr(static_cast<double>(cc) / 255.0)+" mul ";
colorDesc += "exch dup "+FToStr(static_cast<double>(mc) / 255.0)+" mul ";
colorDesc += "exch dup "+FToStr(static_cast<double>(yc) / 255.0)+" mul ";
colorDesc += "exch "+FToStr(static_cast<double>(kc) / 255.0)+" mul\n";
8788,21 → 8783,22
}
colorDesc += "}\n";
PutDoc("/Range [0 1 0 1 0 1 0 1]\n");
PutDoc("/Length "+QString::number(colorDesc.length()+1)+"\n");
PutDoc(">>\nstream\n"+EncStream(colorDesc, spotObject)+"\nendstream\nendobj\n");
PutDoc("/Length "+Pdf::toPdf(colorDesc.length()+1)+"\n");
PutDoc(">>\nstream\n"+EncStream(colorDesc, spotObject)+"\nendstream");
writer.endObj(spotObject);
}
QString tmp;
QByteArray tmp;
if (((Options.Version >= PDFOptions::PDFVersion_14) || (Options.Version == PDFOptions::PDFVersion_X4)) && (transparencyFound))
tmp += "/"+TRes+" gs\n";
tmp += Pdf::toName(TRes) + " gs\n";
if ((forArrow) || (!stroke))
{
tmp += "/Pattern cs\n";
tmp += "/Pattern"+QString::number(patObject)+" scn\n";
tmp += "/Pattern"+Pdf::toPdf(patObject)+" scn\n";
}
else
{
tmp += "/Pattern CS\n";
tmp += "/Pattern"+QString::number(patObject)+" SCN\n";
tmp += "/Pattern"+Pdf::toPdf(patObject)+" SCN\n";
}
output = tmp;
return true;
8832,15 → 8828,15
double y2 = y-ite->height();
PageItem_OSGFrame *osgframe = ite->asOSGFrame();
QList<uint> viewList;
uint viewObj = 0;
PdfId viewObj = 0;
QHash<QString, PageItem_OSGFrame::viewDefinition>::iterator itv;
for (itv = osgframe->viewMap.begin(); itv != osgframe->viewMap.end(); ++itv)
{
uint viewObjL = newObject();
PdfId viewObjL = writer.newObject();
viewList.append(viewObjL);
if (osgframe->currentView == itv.key())
viewObj = viewObjL;
StartObj(viewObjL);
writer.startObj(viewObjL);
PutDoc("<<\n/Type /3DView\n");
PutDoc("/MS /M\n");
PutDoc("/C2W ["+osgframe->getPDFMatrix(itv.key())+" ]\n");
8860,16 → 8856,17
}
PutDoc("/XN ("+PDFEncode(itv.key())+")\n");
PutDoc("/IN ("+PDFEncode(itv.key())+")\n");
PutDoc(">>\nendobj\n");
PutDoc(">>");
writer.endObj(viewObjL);
}
uint appearanceObj = newObject();
StartObj(appearanceObj);
PdfId appearanceObj = writer.newObject();
writer.startObj(appearanceObj);
PutDoc("<<\n/Type /3D\n");
PutDoc("/Subtype /PRC\n");
PutDoc("/VA [");
for (int vl = 0; vl < viewList.count(); vl++)
{
PutDoc(QString::number(viewList.at(vl))+" 0 R ");
PutDoc(Pdf::toPdf(viewList.at(vl))+" 0 R ");
}
PutDoc("]\n");
QByteArray dataP;
8883,30 → 8880,32
dataP = compData;
}
}
PutDoc("/Length "+QString::number(dataP.size()+1)+"\n");
PutDoc("/Length "+Pdf::toPdf(dataP.size()+1)+"\n");
PutDoc(">>\nstream\n");
EncodeArrayToStream(dataP, appearanceObj);
PutDoc("\nendstream\nendobj\n");
uint annotationObj = newObject();
StartObj(annotationObj);
Seite.AObjects.append(annotationObj);
PutDoc("\nendstream");
writer.endObj(appearanceObj);
PdfId appearanceObj = writer.newObject();
writer.startObj(annotationObj);
pageData.AObjects.append(annotationObj);
PutDoc("<<\n/Type /Annot\n");
PutDoc("/Subtype /3D\n");
PutDoc("/F 4\n");
PutDoc("/3DD "+QString::number(appearanceObj)+" 0 R\n");
PutDoc("/3DV "+QString::number(viewObj)+" 0 R\n");
PutDoc("/3DD "+Pdf::toPdf(appearanceObj)+" 0 R\n");
PutDoc("/3DV "+Pdf::toPdf(viewObj)+" 0 R\n");
PutDoc("/3DA <<\n/A /PV\n/TB true\n/NP true\n>>\n");
QString onState = QString("/%1").arg(ite->itemName().replace(".", "_" ));
QByteArray onState = Pdf::toName(ite->itemName().replace(".", "_" ));
PutDoc("/AS "+onState+"\n");
uint appearanceObj1 = newObject();
PutDoc("/AP << /N <<\n" + onState + " " + QString::number(appearanceObj1)+" 0 R >> >>\n");
PdfId appearanceObj1 = writer.newObject();
PutDoc("/AP << /N <<\n" + onState + " " + Pdf::toPdf(appearanceObj1)+" 0 R >> >>\n");
PutDoc("/Rect [ "+FToStr(x+bleedDisplacementX)+" "+FToStr(y2+bleedDisplacementY)+" "+FToStr(x2+bleedDisplacementX)+" "+FToStr(y+bleedDisplacementY)+" ]\n");
PutDoc(">>\nendobj\n");
PutDoc(">>");
writer.endObj(appearanceObj);
if (!ite->Pfile.isEmpty())
{
PDF_Image(ite, ite->Pfile, ite->imageXScale(), ite->imageYScale(), ite->imageXOffset(), -ite->imageYOffset(), true);
QString cc = QString::number(ite->pixm.width())+" 0 0 "+QString::number(ite->pixm.height())+" 0 0 cm\n";
cc += "/"+ResNam+"I"+QString::number(ResCount-1)+" Do";
QByteArray cc = Pdf::toPdf(ite->pixm.width())+" 0 0 "+Pdf::toPdf(ite->pixm.height())+" 0 0 cm\n";
cc += Pdf::toName(ResNam+"I"+Pdf::toPdf(ResCount-1)) + " Do";
PDF_xForm(appearanceObj1, ite->pixm.width(), ite->pixm.height(), cc);
}
delete tempImageFile;
8917,15 → 8916,15
void PDFLibCore::PDF_RadioButtons()
{
QMap<PageItem*, QList<PageItem*> > rbMap;
for (int a = 0; a < Seite.radioButtonList.count(); a++)
for (int a = 0; a < pageData.radioButtonList.count(); a++)
{
PageItem* pa = Seite.radioButtonList[a]->Parent;
PageItem* pa = pageData.radioButtonList[a]->Parent;
if (rbMap.contains(pa))
rbMap[pa].append(Seite.radioButtonList[a]);
rbMap[pa].append(pageData.radioButtonList[a]);
else
{
QList<PageItem*> aList;
aList.append(Seite.radioButtonList[a]);
aList.append(pageData.radioButtonList[a]);
rbMap.insert(pa, aList);
}
}
8933,47 → 8932,48
for (it = rbMap.begin(); it != rbMap.end(); ++it)
{
QList<PageItem*> bList = it.value();
QList<uint> kidsList;
uint parentObject = newObject();
QString onState = "";
QString anTitle;
QList<PdfId> kidsList;
PdfId parentObject = writer.newObject();
QByteArray onState = "";
QByteArray anTitle;
if (it.key() == 0)
anTitle = QString("Page%1").arg(ActPageP->pageNr() + 1);
anTitle = "Page" + Pdf::toPdf(ActPageP->pageNr() + 1);
else
anTitle = QString("/%1").arg(it.key()->itemName().replace(".", "_" ));
anTitle = Pdf::toName(it.key()->itemName().replace(".", "_" ));
for (int a = 0; a < bList.count(); a++)
{
uint kid = PDF_RadioButton(bList[a], parentObject, anTitle);
PdfId kid = PDF_RadioButton(bList[a], parentObject, anTitle);
kidsList.append(kid);
if (bList[a]->annotation().IsChk())
onState = QString("/%1").arg(bList[a]->itemName().replace(".", "_" ));
onState = Pdf::toName(bList[a]->itemName().replace(".", "_" ));
}
StartObj(parentObject);
Seite.AObjects.append(parentObject);
Seite.FormObjects.append(parentObject);
writer.startObj(parentObject);
pageData.AObjects.append(parentObject);
pageData.FormObjects.append(parentObject);
PutDoc("<<\n/Type /Annot\n");
PutDoc("/Subtype /Widget\n");
PutDoc("/FT /Btn\n");
PutDoc("/T " + EncString(anTitle, parentObject) + "\n");
PutDoc("/Contents " + EncStringUTF16(anTitle, parentObject) + "\n");
PutDoc("/Ff "+QString::number(Annotation::Flag_Radio | Annotation::Flag_NoToggleToOff)+"\n");
PutDoc("/Ff "+Pdf::toPdf(Annotation::Flag_Radio | Annotation::Flag_NoToggleToOff)+"\n");
PutDoc("/V "+onState+"\n");
PutDoc("/DV "+onState+"\n");
PutDoc("/Kids\n[\n");
for (int a = 0; a < kidsList.count(); a++)
{
PutDoc(QString::number(kidsList[a]) + " 0 R\n");
PutDoc(Pdf::toPdf(kidsList[a]) + " 0 R\n");
}
PutDoc("]\n");
PutDoc("/Rect [0 0 0 0]\n");
PutDoc(">>\nendobj\n");
PutDoc(">>");
writer.endObj(parentObject);
}
}
 
uint PDFLibCore::PDF_RadioButton(PageItem* ite, uint parent, QString parentName)
PdfId PDFLibCore::PDF_RadioButton(PageItem* ite, PdfId parent, QString parentName)
{
QMap<int, QString> ind2PDFabr;
static const QString bifonts[] = {"/Courier", "/Courier-Bold", "/Courier-Oblique", "/Courier-BoldOblique",
QMap<int, QByteArray> ind2PDFabr;
static const QByteArray bifonts[] = {"/Courier", "/Courier-Bold", "/Courier-Oblique", "/Courier-BoldOblique",
"/Helvetica", "/Helvetica-Bold", "/Helvetica-Oblique", "/Helvetica-BoldOblique",
"/Times-Roman", "/Times-Bold", "/Times-Italic", "/Times-BoldItalic",
"/ZapfDingbats", "/Symbol"};
8984,25 → 8984,25
double y = ActPageP->height() - (ite->yPos() - ActPageP->yOffset());
double x2 = x+ite->width();
double y2 = y-ite->height();
QString cc;
uint annotationObj = newObject();
uint actionObj = 0;
QByteArray cc;
PdfId annotationObj = writer.newObject();
PdfId actionObj = 0;
if ((ite->annotation().Type() > 1) && ((ite->annotation().ActionType() == Annotation::Action_JavaScript) || (ite->annotation().AAact())) && (!ite->annotation().Action().isEmpty()))
actionObj = WritePDFString(ite->annotation().Action());
uint AActionObj = writeActions(ite->annotation(), annotationObj);
StartObj(annotationObj);
Seite.AObjects.append(annotationObj);
PdfId AActionObj = writeActions(ite->annotation(), annotationObj);
writer.startObj(annotationObj);
pageData.AObjects.append(annotationObj);
PutDoc("<<\n/Type /Annot\n");
PutDoc("/Subtype /Widget\n");
PutDoc("/Parent " + QString::number(parent) + " 0 R\n");
PutDoc("/Parent " + Pdf::toPdf(parent) + " 0 R\n");
PutDoc("/Contents " + EncStringUTF16(parentName, annotationObj) + "\n");
if (!ite->annotation().ToolTip().isEmpty())
PutDoc("/TU " + EncStringUTF16(ite->annotation().ToolTip(), annotationObj) + "\n");
PutDoc("/F ");
QString mm[] = {"4", "2", "0", "32"};
QByteArray mm[] = {"4", "2", "0", "32"};
PutDoc(mm[ite->annotation().Vis()]);
PutDoc("\n");
QString cnx = "/"+StdFonts["/ZapfDingbats"];
QByteArray cnx = Pdf::toName(StdFonts["/ZapfDingbats"]);
cnx += " "+FToStr(ite->itemText.defaultStyle().charStyle().fontSize() / 10.0)+" Tf";
if (ite->itemText.defaultStyle().charStyle().fillColor() != CommonStrings::None)
cnx += " "+ putColor(ite->itemText.defaultStyle().charStyle().fillColor(), ite->itemText.defaultStyle().charStyle().fillShade(), true);
9012,12 → 9012,12
int flg = ite->annotation().Flag();
if (Options.Version == PDFOptions::PDFVersion_13)
flg = flg & 522247;
PutDoc("/Ff "+QString::number(flg)+"\n");
PutDoc("/Ff "+Pdf::toPdf(flg)+"\n");
PutDoc("/FT /Btn\n");
PutDoc("/BS << /Type /Border /W ");
PutDoc(ite->annotation().borderColor() != CommonStrings::None ? QString::number(ite->annotation().Bwid()) : QString("0"));
PutDoc(ite->annotation().borderColor() != CommonStrings::None ? Pdf::toPdf(ite->annotation().Bwid()) : "0");
PutDoc(" /S /");
const QString xb[] = {"S", "D", "U", "B", "I"};
const QByteArray xb[] = {"S", "D", "U", "B", "I"};
PutDoc(xb[ite->annotation().Bsty()]);
PutDoc(" >>\n");
PutDoc("/MK << ");
9025,18 → 9025,18
if (ite->annotation().borderColor() != CommonStrings::None)
PutDoc("/BC [ "+SetColor(ite->annotation().borderColor(), 100)+" ] ");
if (ite->rotation() != 0)
PutDoc("/R "+QString::number((abs(static_cast<int>(ite->rotation())) / 90)*90)+" ");
PutDoc("/R "+Pdf::toPdf((abs(static_cast<int>(ite->rotation())) / 90)*90)+" ");
PutDoc(">>\n");
QString onState = QString("/%1").arg(ite->itemName().replace(".", "_" ));
QByteArray onState = Pdf::toName(ite->itemName().replace(".", "_" ));
if (ite->annotation().IsChk())
PutDoc("/AS "+onState+"\n");
else
PutDoc("/AS /Off\n");
uint appearanceObj1 = newObject();
uint appearanceObj2 = newObject();
PdfId appearanceObj1 = writer.newObject();
PdfId appearanceObj2 = writer.newObject();
PutDoc("/AP << /N <<\n");
PutDoc(onState + " " + QString::number(appearanceObj1)+" 0 R\n");
PutDoc("/Off " + QString::number(appearanceObj2)+" 0 R\n");
PutDoc(onState + " " + Pdf::toObjRef(appearanceObj1)+"\n");
PutDoc("/Off " + Pdf::toObjRef(appearanceObj2)+"\n");
PutDoc(">> >>\n");
if ((ite->annotation().ActionType() != Annotation::Action_None) || (ite->annotation().AAact()))
{
9044,7 → 9044,7
{
if (!ite->annotation().Action().isEmpty())
{
PutDoc("/A << /Type /Action /S /JavaScript /JS " + QString::number(actionObj) + " 0 R >>\n");
PutDoc("/A << /Type /Action /S /JavaScript /JS " + Pdf::toPdf(actionObj) + " 0 R >>\n");
}
}
if (ite->annotation().AAact())
9051,9 → 9051,9
{
if (!ite->annotation().Action().isEmpty())
{
PutDoc("/A << /Type /Action /S /JavaScript /JS " + QString::number(actionObj) + " 0 R >>\n");
PutDoc("/A << /Type /Action /S /JavaScript /JS " + Pdf::toPdf(actionObj) + " 0 R >>\n");
}
PutDoc("/AA " + QString::number(AActionObj) + " 0 R\n");
PutDoc("/AA " + Pdf::toPdf(AActionObj) + " 0 R\n");
}
}
switch (((abs(static_cast<int>(ite->rotation())) / 90)*90))
9080,7 → 9080,8
break;
}
PutDoc("/Rect [ "+FToStr(x+bleedDisplacementX)+" "+FToStr(y2+bleedDisplacementY)+" "+FToStr(x2+bleedDisplacementX)+" "+FToStr(y+bleedDisplacementY)+" ]\n");
PutDoc(">>\nendobj\n");
PutDoc(">>");
writer.endObj(annotationObj);
cc = createBorderAppearance(ite);
if (ite->itemText.defaultStyle().charStyle().fillColor() != CommonStrings::None)
cc += putColor(ite->itemText.defaultStyle().charStyle().fillColor(), ite->itemText.defaultStyle().charStyle().fillShade(), true);
9107,8 → 9108,8
ScImage img;
ScImage img2;
ScImage img3;
QMap<int, QString> ind2PDFabr;
static const QString bifonts[] = {"/Courier", "/Courier-Bold", "/Courier-Oblique", "/Courier-BoldOblique",
QMap<int, QByteArray> ind2PDFabr;
static const QByteArray bifonts[] = {"/Courier", "/Courier-Bold", "/Courier-Oblique", "/Courier-BoldOblique",
"/Helvetica", "/Helvetica-Bold", "/Helvetica-Oblique", "/Helvetica-BoldOblique",
"/Times-Roman", "/Times-Bold", "/Times-Italic", "/Times-BoldItalic",
"/ZapfDingbats", "/Symbol"};
9120,9 → 9121,6
double x2 = x+ite->width();
double y2 = y-ite->height();
QString bmUtf16("");
QString cc;
QFileInfo fiBase(Spool.fileName());
QString baseDir = fiBase.absolutePath();
if (!((ite->itemText.length() == 1) && (ite->itemText.text(0, 1) == QChar(13))))
{
// #6823 EncStringUTF16() perform the string encoding by its own
9129,28 → 9127,28
// via EncodeUTF16() so bmUtf16 must not encoded before
for (uint d = 0; d < static_cast<uint>(ite->itemText.length()); ++d)
{
cc = ite->itemText.text(d, 1);
QString cc = ite->itemText.text(d, 1);
bmUtf16 += (cc == QChar(13) ? QChar(10) : cc);
}
}
QString anTitle = ite->itemName().replace(".", "_" );
QByteArray anTitle = Pdf::toPdfDocEncoding(ite->itemName().replace(".", "_" ));
QStringList bmstUtf16 = bmUtf16.split(QChar(10), QString::SkipEmptyParts);
const QString m[] = {"4", "5", "F", "l", "H", "n"};
QString ct(m[ite->annotation().ChkStil()]);
uint annotationObj = newObject();
uint appearanceObj = 0;
uint appearanceObj1 = 0;
uint appearanceObj2 = 0;
uint icon1Obj = 0;
uint icon2Obj = 0;
uint icon3Obj = 0;
uint actionObj = 0;
const QByteArray m[] = {"4", "5", "F", "l", "H", "n"};
QByteArray ct(m[ite->annotation().ChkStil()]);
PdfId annotationObj = writer.newObject();
PdfId appearanceObj = 0;
PdfId appearanceObj1 = 0;
PdfId appearanceObj2 = 0;
PdfId icon1Obj = 0;
PdfId icon2Obj = 0;
PdfId icon3Obj = 0;
PdfId actionObj = 0;
if ((ite->annotation().Type() > 1) && ((ite->annotation().ActionType() == Annotation::Action_JavaScript) || (ite->annotation().AAact())) && (!ite->annotation().Action().isEmpty()))
actionObj = WritePDFString(ite->annotation().Action());
uint AActionObj = writeActions(ite->annotation(), annotationObj);
StartObj(annotationObj);
Seite.AObjects.append(annotationObj);
QString onState = QString("/%1").arg(ite->itemName().replace(".", "_" ));
PdfId AActionObj = writeActions(ite->annotation(), annotationObj);
writer.startObj(annotationObj);
pageData.AObjects.append(annotationObj);
QByteArray onState = Pdf::toName(ite->itemName().replace(".", "_" ));
PutDoc("<<\n/Type /Annot\n");
switch (ite->annotation().Type())
{
9203,10 → 9201,10
PutDoc("/Subtype /Link\n");
if (ite->annotation().ActionType() == Annotation::Action_GoTo)
{
PutDoc("/Dest /"+NDnam+QString::number(NDnum)+"\n");
Dest de;
de.Na