1 /*
2 For general Scribus (>=1.3.2) copyright and licensing information please refer
3 to the COPYING file provided with the program. Following this notice may exist
4 a copyright and/or license notice that predates the release of Scribus 1.3.2
5 for which a new license (GPL+exception) is in place.
6 */
7 #include <vector>
8 #include <iostream>
9 
10 #include "objprinter.h"
11 
12 #include <structmember.h>
13 #include <QFileInfo>
14 #include <QDir>
15 #include <QScopedPointer>
16 
17 #include "cmdutil.h"
18 #include "prefsmanager.h"
19 #include "pslib.h"
20 #include "scpaths.h"
21 #include "scprintengine_pdf.h"
22 #include "scprintengine_ps.h"
23 #include "scribuscore.h"
24 #include "scribusdoc.h"
25 #include "util_file.h"
26 #include "util_ghostscript.h"
27 #include "util_printer.h"
28 
29 #if defined(_WIN32)
30 #include "scprintengine_gdi.h"
31 #endif
32 
33 // these functions are located at utils.cpp
34 void SCRIBUS_API ReOrderText(ScribusDoc *doc, ScribusView *view);
35 // end of utils.cpp
36 
37 typedef struct
38 {
39 	PyObject_HEAD
40 	PyObject *allPrinters; // list of strings - names of installed printers
41 	PyObject *printer;  // string - selected printer
42 	PyObject *file;  // string - name of file to print into (eg. output.ps)
43 	PyObject *cmd; // string - if "" use standard command else use this as command (eg. "kprinter", "xpp" ...)
44 	PyObject *pages; // list of integers - pages to be printed
45 	int copies; // numer of printed copies
46 	PyObject *separation; // string - No; All; Cyan; Magenta; Yellow; Black
47 	int color; // bool - do we print in color=1 or greyscale=0
48 	int useICC; // bool - do we use ICC Profiles 0 = No 1 = Yes
49 	int prnLang; // integer - print language
50 	int mph; // bool - mirror pages horizontally
51 	int mpv; // bool - mirror pages vertically
52 	int ucr; // bool - Under Color Removal
53 } Printer;
54 
55 
Printer_dealloc(Printer * self)56 static void Printer_dealloc(Printer* self)
57 {
58 	Py_XDECREF(self->allPrinters);
59 	Py_XDECREF(self->printer);
60 	Py_XDECREF(self->file);
61 	Py_XDECREF(self->cmd);
62 	Py_XDECREF(self->pages);
63 	Py_XDECREF(self->separation);
64 	self->ob_type->tp_free((PyObject *)self);
65 }
66 
Printer_new(PyTypeObject * type,PyObject *,PyObject *)67 static PyObject * Printer_new(PyTypeObject *type, PyObject * /*args*/, PyObject * /*kwds*/)
68 {
69 // do not create new object if there is no opened document
70 	if (!checkHaveDocument()) {
71 		return nullptr;
72 	}
73 
74 	Printer *self = (Printer *)type->tp_alloc(type, 0);
75 	if (self != nullptr) {
76 // set allPrinters attribute
77 		self->allPrinters = PyList_New(0);
78 		if (self->allPrinters == nullptr) {
79 			Py_DECREF(self);
80 			return nullptr;
81 		}
82 // set printer attribute
83 		self->printer = PyString_FromString("");
84 		if (self->printer == nullptr) {
85 			Py_DECREF(self);
86 			return nullptr;
87 		}
88 // set file attribute
89 		self->file = PyString_FromString("");
90 		if (self->file == nullptr) {
91 			Py_DECREF(self);
92 			return nullptr;
93 		}
94 // set cmd attribute
95 		self->cmd = PyString_FromString("");
96 		if (self->cmd == nullptr) {
97 			Py_DECREF(self);
98 			return nullptr;
99 		}
100 // set pages attribute
101 		self->pages = PyList_New(0);
102 		if (self->pages == nullptr) {
103 			Py_DECREF(self);
104 			return nullptr;
105 		}
106 // set separation attribute
107 		self->separation = PyString_FromString("No");
108 		if (self->separation == nullptr) {
109 			Py_DECREF(self);
110 			return nullptr;
111 		}
112 // set color attribute
113 		self->color = 1;
114 // set useICC attribute
115 		self->useICC = 0;
116 // set prnLanguage attribute
117 		self->prnLang = (int) PrintLanguage::PostScript3;
118 // set mph attribute
119 		self->mph = 0;
120 // set mpv attribute
121 		self->mpv = 0;
122 // set ucr attribute
123 		self->ucr = 1;
124 // set copies attribute
125 		self->copies = 1;
126 	}
127 	return (PyObject *) self;
128 }
129 
Printer_init(Printer * self,PyObject *,PyObject *)130 static int Printer_init(Printer *self, PyObject * /*args*/, PyObject * /*kwds*/)
131 {
132 	if (!checkHaveDocument()) {
133 		return -1;
134 	}
135 // pool system for installed printers
136 // most code is stolen and little adopted from druck.cpp
137 	PyObject *allPrinters = PyList_New(0);
138 	if (allPrinters) {
139 		Py_DECREF(self->allPrinters);
140 		self->allPrinters = allPrinters;
141 	}
142 	QStringList printers = PrinterUtil::getPrinterNames();
143 	for (int i = 0; i < printers.count(); ++i)
144 	{
145 		QString prn = printers[i];
146 		if (prn.isEmpty())
147 			continue;
148 		PyObject *tmppr = PyString_FromString(prn.toLocal8Bit().constData());
149 		if (tmppr) {
150 			PyList_Append(self->allPrinters, tmppr);
151 			Py_DECREF(tmppr);
152 		}
153 	}
154 	PyObject *tmp2 = PyString_FromString("File");
155 	PyList_Append(self->allPrinters, tmp2);
156 	Py_DECREF(tmp2);
157 // as defaut set to print into file
158 	PyObject *printer = nullptr;
159 	printer = PyString_FromString("File");
160 	if (printer) {
161 		Py_DECREF(self->printer);
162 		self->printer = printer;
163 	}
164 // set defaul name of file to print into
165 	QString tf(ScCore->primaryMainWindow()->doc->pdfOptions().fileName);
166 	if (tf.isEmpty()) {
167 		QFileInfo fi = QFileInfo(ScCore->primaryMainWindow()->doc->documentFileName());
168 		tf = fi.path() + "/" + fi.baseName() + ".pdf";
169 	}
170 	PyObject *file = nullptr;
171 	file = PyString_FromString(tf.toLatin1());
172 	if (file) {
173 		Py_DECREF(self->file);
174 		self->file = file;
175 	} else {
176 		PyErr_SetString(PyExc_SystemError, "Can not initialize 'file' attribute");
177 		return -1;
178 	}
179 // alternative printer commands default to ""
180 	PyObject *cmd = nullptr;
181 	cmd = PyString_FromString("");
182 	if (cmd) {
183 		Py_DECREF(self->cmd);
184 		self->cmd = cmd;
185 	}
186 // if document exist when created Printer instance
187 // set to print all pages
188 	PyObject *pages = nullptr;
189 	int num = ScCore->primaryMainWindow()->doc->Pages->count();
190 	pages = PyList_New(num);
191 	if (pages) {
192 		Py_DECREF(self->pages);
193 		self->pages = pages;
194 	}
195 	for (int i = 0; i < num; i++) {
196 		PyObject *tmp = nullptr;
197 		tmp = PyInt_FromLong((long)i+1L); // instead of 1 put here first page number
198 		if (tmp)
199 			PyList_SetItem(self->pages, i, tmp);
200 	}
201 // do not print separation
202 	PyObject *separation = nullptr;
203 	separation = PyString_FromString("No");
204 	if (separation) {
205 		Py_DECREF(self->separation);
206 		self->separation = separation;
207 	}
208 // print in color
209 	self->color = 1;
210 // do not use ICC Profile
211 	self->useICC = 0;
212 // use PostScrip level 3
213 	self->prnLang = (int) PrintLanguage::PostScript3;
214 // do not mirror pages
215 	self->mph = 0;
216 // do not mirror pages
217 	self->mpv = 0;
218 // apply Under Color Removal as default
219 	self->ucr = 1;
220 // number of copies
221 	self->copies = 1;
222 	return 0;
223 }
224 
225 static PyMemberDef Printer_members[] = {
226 	{const_cast<char*>("copies"), T_INT, offsetof(Printer, copies), 0, const_cast<char*>("Number of copies")},
227 	{const_cast<char*>("color"), T_INT, offsetof(Printer, color), 0, const_cast<char*>("Print in color.\n\t True - color  --  Default\n\t False - greyscale")},
228 	{const_cast<char*>("useICC"), T_INT, offsetof(Printer, useICC), 0, const_cast<char*>("Use ICC Profile\n\tTrue\n\tFalse  --  Default")},
229 	{const_cast<char*>("pslevel"), T_INT, offsetof(Printer, prnLang), 0, const_cast<char*>("Deprecated, use prnLanguage instead.")}, // Deprecated
230 	{const_cast<char*>("prnLanguage"), T_INT, offsetof(Printer, prnLang), 0, const_cast<char*>("Print Language\nOne of PRNLANG_* constants  -- Default is PRNLANG_POSTSCRIPT3.")},
231 	{const_cast<char*>("mph"), T_INT, offsetof(Printer, mph), 0, const_cast<char*>("Mirror Pages Horizontal\n\tTrue\n\tFalse  --  Default")},
232 	{const_cast<char*>("mpv"), T_INT, offsetof(Printer, mpv), 0, const_cast<char*>("Mirror Pages Vertical\n\t True\n\tFalse  --  Default")},
233 	{const_cast<char*>("ucr"), T_INT, offsetof(Printer, ucr), 0, const_cast<char*>("Apply Under Color Removal\n\tTrue  --  Default\n\tFalse")},
234 	{nullptr, 0, 0, 0, nullptr} // sentinel
235 };
236 
237 /* Here begins Getter & Setter functions */
238 
Printer_getallPrinters(Printer * self,void *)239 static PyObject *Printer_getallPrinters(Printer *self, void * /*closure*/)
240 {
241 	Py_INCREF(self->allPrinters);
242 	return self->allPrinters;
243 }
244 
Printer_setallPrinters(Printer *,PyObject *,void *)245 static int Printer_setallPrinters(Printer * /*self*/, PyObject * /*value*/, void * /*closure*/)
246 {
247 	PyErr_SetString(PyExc_ValueError, "'allPrinters' attribute is READ-ONLY");
248 	return -1;
249 }
250 
Printer_getprinter(Printer * self,void *)251 static PyObject *Printer_getprinter(Printer *self, void * /*closure*/)
252 {
253 	Py_INCREF(self->printer);
254 	return self->printer;
255 }
256 
Printer_setprinter(Printer * self,PyObject * value,void *)257 static int Printer_setprinter(Printer *self, PyObject *value, void * /*closure*/)
258 {
259 	if (value == nullptr) {
260 		PyErr_SetString(PyExc_TypeError, "Cannot delete 'printer' attribute.");
261 		return -1;
262 	}
263 	if (!PyString_Check(value)) {
264 		PyErr_SetString(PyExc_TypeError, "The 'printer' attribute value must be string.");
265 		return -1;
266 	}
267 
268 	int n = PyList_Size(self->allPrinters);
269 	bool same = 0;
270 	for (int i = 0; i < n; i++) {
271 		if (PyObject_RichCompareBool(value, PyList_GetItem(self->allPrinters, i), Py_EQ) == 1) {
272 			same = true;
273 			break;
274 		}
275 	}
276 	if (!same) {
277 		PyErr_SetString(PyExc_ValueError, "'printer' value can be only one of string in 'allPrinters' attribute ");
278 		return -1;
279 	}
280 
281 	Py_DECREF(self->printer);
282 	Py_INCREF(value);
283 	self->printer = value;
284 	return 0;
285 }
286 
Printer_getfile(Printer * self,void *)287 static PyObject *Printer_getfile(Printer *self, void * /*closure*/)
288 {
289 	Py_INCREF(self->file);
290 	return self->file;
291 }
292 
Printer_setfile(Printer * self,PyObject * value,void *)293 static int Printer_setfile(Printer *self, PyObject *value, void * /*closure*/)
294 {
295 	if (value == nullptr) {
296 		PyErr_SetString(PyExc_TypeError, "Cannot delete 'file' attribute.");
297 		return -1;
298 	}
299 	if (!PyString_Check(value)) {
300 		PyErr_SetString(PyExc_TypeError, "The 'file' attribute value must be string.");
301 		return -1;
302 	}
303 	Py_DECREF(self->file);
304 	Py_INCREF(value);
305 	self->file = value;
306 	return 0;
307 }
308 
Printer_getcmd(Printer * self,void *)309 static PyObject *Printer_getcmd(Printer *self, void * /*closure*/)
310 {
311 	Py_INCREF(self->cmd);
312 	return self->cmd;
313 }
314 
Printer_setcmd(Printer * self,PyObject * value,void *)315 static int Printer_setcmd(Printer *self, PyObject *value, void * /*closure*/)
316 {
317 	if (value == nullptr) {
318 		PyErr_SetString(PyExc_TypeError, "Cannot delete 'cmd' attribute.");
319 		return -1;
320 	}
321 	if (!PyString_Check(value)) {
322 		PyErr_SetString(PyExc_TypeError, "The 'cmd' attribute value must be string.");
323 		return -1;
324 	}
325 	Py_DECREF(self->cmd);
326 	Py_INCREF(value);
327 	self->cmd = value;
328 	return 0;
329 }
330 
Printer_getpages(Printer * self,void *)331 static PyObject *Printer_getpages(Printer *self, void * /*closure*/)
332 {
333 	Py_INCREF(self->pages);
334 	return self->pages;
335 }
336 
Printer_setpages(Printer * self,PyObject * value,void *)337 static int Printer_setpages(Printer *self, PyObject *value, void * /*closure*/)
338 {
339 	if (value == nullptr) {
340 		PyErr_SetString(PyExc_TypeError, "Cannot delete 'pages' attribute.");
341 		return -1;
342 	}
343 	if (!PyList_Check(value)) {
344 		PyErr_SetString(PyExc_TypeError, "'pages' attribute value must be list of integers.");
345 		return -1;
346 	}
347 	int len = PyList_Size(value);
348 	for (int i = 0; i < len; i++) {
349 		PyObject *tmp = PyList_GetItem(value, i);
350 		if (!PyInt_Check(tmp)) {
351 			PyErr_SetString(PyExc_TypeError, "'pages' attribute must be list containing only integers.");
352 			return -1;
353 		}
354 		if (PyInt_AsLong(tmp) > static_cast<int>(ScCore->primaryMainWindow()->doc->Pages->count()) || PyInt_AsLong(tmp) < 1) {
355 			PyErr_SetString(PyExc_ValueError, "'pages' value out of range.");
356 			return -1;
357 		}
358 	}
359 	Py_DECREF(self->pages);
360 	Py_INCREF(value);
361 	self->pages = value;
362 	return 0;
363 }
364 
Printer_getseparation(Printer * self,void *)365 static PyObject *Printer_getseparation(Printer *self, void * /*closure*/)
366 {
367 	Py_INCREF(self->separation);
368 	return self->separation;
369 }
370 
Printer_setseparation(Printer * self,PyObject * value,void *)371 static int Printer_setseparation(Printer *self, PyObject *value, void * /*closure*/)
372 {
373 	if (value == nullptr) {
374 		PyErr_SetString(PyExc_TypeError, "Cannot delete 'separation' attribute.");
375 		return -1;
376 	}
377 	if (!PyString_Check(value)) {
378 		PyErr_SetString(PyExc_TypeError, "The 'separation' attribute value must be string.");
379 		return -1;
380 	}
381 	Py_DECREF(self->separation);
382 	Py_INCREF(value);
383 	self->separation = value;
384 	return 0;
385 }
386 
387 
388 static PyGetSetDef Printer_getseters [] = {
389 	{const_cast<char*>("allPrinters"), (getter)Printer_getallPrinters, (setter)Printer_setallPrinters, const_cast<char*>("List of installed printers  --  read only"), nullptr},
390 	{const_cast<char*>("printer"), (getter)Printer_getprinter, (setter)Printer_setprinter, const_cast<char*>("Name of printer to use.\nDefault is 'File' for printing into file"), nullptr},
391 	{const_cast<char*>("file"), (getter)Printer_getfile, (setter)Printer_setfile, const_cast<char*>("Name of file to print into"), nullptr},
392 	{const_cast<char*>("cmd"), (getter)Printer_getcmd, (setter)Printer_setcmd, const_cast<char*>("Alternative Printer Command"), nullptr},
393 	{const_cast<char*>("pages"), (getter)Printer_getpages, (setter)Printer_setpages, const_cast<char*>("List of pages to be printed"), nullptr},
394 	{const_cast<char*>("separation"), (getter)Printer_getseparation, (setter)Printer_setseparation, const_cast<char*>("Print separationl\n\t 'No'  -- Default\n\t 'All'\n\t 'Cyan'\n\t 'Magenta'\n\t 'Yellow'\n\t 'Black'\nBeware of misspelling because check is not performed"), nullptr},
395 	{nullptr, nullptr, nullptr, nullptr, nullptr}  // sentinel
396 };
397 
398 // Here we actually print
Printer_print(Printer * self)399 static PyObject *Printer_print(Printer *self)
400 {
401 	if (!checkHaveDocument()) {
402 		return nullptr;
403 	}
404 
405 //	ReOrderText(ScCore->primaryMainWindow()->doc, ScCore->primaryMainWindow()->view);
406 	QString prn = QString(PyString_AsString(self->printer));
407 	QString fna = QString(PyString_AsString(self->file));
408 	bool    fil = QString(PyString_AsString(self->printer)) == QString("File");
409 	QString sepName = QString(PyString_AsString(self->separation));
410 
411 	PrintOptions options;
412 	for (int i = 0; i < PyList_Size(self->pages); ++i) {
413 		options.pageNumbers.push_back((int) PyInt_AsLong(PyList_GetItem(self->pages, i)));
414 	}
415 	options.printer   = prn;
416 	options.prnLanguage = (PrintLanguage) self->prnLang;
417 	options.copies    = (self->copies < 1) ? 1 : self->copies;
418 	options.toFile    = fil;
419 	options.filename  = fil ? fna : QString();
420 	options.separationName = sepName;
421 	options.outputSeparations = sepName != QString("No");
422 	options.useSpotColors = true;
423 	options.useColor = self->color;
424 	options.mirrorH  = self->mph;
425 	options.mirrorV  = self->mpv;
426 	options.doGCR    = self->ucr;
427 	options.doClip = false;
428 	options.setDevParam = false;
429 	options.cropMarks  = false;
430 	options.bleedMarks = false;
431 	options.registrationMarks = false;
432 	options.colorMarks = false;
433 	options.includePDFMarks = false;
434 	options.markOffset = 0.0;
435 	options.bleeds.set(0, 0, 0, 0);
436 	if (!PrinterUtil::checkPrintLanguageSupport(options.printer, options.prnLanguage, options.toFile))
437 		options.prnLanguage = PrinterUtil::getDefaultPrintLanguage(options.printer, options.toFile);
438 	if (options.prnLanguage == PrintLanguage::PDF || options.prnLanguage == PrintLanguage::WindowsGDI)
439 	{
440 		options.separationName = "All";
441 		options.outputSeparations = false;
442 	}
443 	options.printerCommand = QString(PyString_AsString(self->cmd));
444 
445 	ScribusDoc* currentDoc = ScCore->primaryMainWindow()->doc;
446 
447 #if defined(_WIN32)
448 	if (!options.toFile)
449 	{
450 		QByteArray devMode;
451 		bool printDone = false;
452 		if (PrinterUtil::getDefaultSettings(prn, options.devMode))
453 		{
454 			ScPrintEngine_GDI winPrint(*currentDoc);
455 			printDone = winPrint.print(options);
456 		}
457 		if (!printDone)
458 			PyErr_SetString(PyExc_SystemError, "Printing failed");
459 		Py_RETURN_NONE;
460 	}
461 #endif
462 
463 	if (options.prnLanguage == PrintLanguage::PostScript1 || options.prnLanguage == PrintLanguage::PostScript2)
464 	{
465 		if (!ScCore->haveGS())
466 		{
467 			PyErr_SetString(PyExc_SystemError, "Printing failed : GhostScript is needed to print to PostScript Level 1 or Level 2");
468 			Py_RETURN_NONE;
469 		}
470 	}
471 
472 	QScopedPointer<ScPrintEngine> printEngine;
473 	if (options.prnLanguage == PrintLanguage::PDF)
474 		printEngine.reset(new ScPrintEngine_PDF(*currentDoc));
475 	else
476 		printEngine.reset(new ScPrintEngine_PS(*currentDoc));
477 
478 	bool printDone = printEngine->print(options);
479 	if (!printDone)
480 	{
481 		QString error = printEngine->errorMessage();
482 		PyErr_SetString(PyExc_SystemError, error.toLocal8Bit().constData());
483 	}
484 
485 	Py_RETURN_NONE;
486 }
487 
488 static PyMethodDef Printer_methods[] = {
489 	{const_cast<char*>("printNow"), (PyCFunction)Printer_print, METH_NOARGS, printer_printnow__doc__},
490 	{nullptr, (PyCFunction)(nullptr), 0, nullptr} // sentinel
491 };
492 
493 PyTypeObject Printer_Type = {
494 	PyObject_HEAD_INIT(nullptr)   // PyObject_VAR_HEAD
495 	0,			 //
496 	const_cast<char*>("scribus.Printer"), // char *tp_name; /* For printing, in format "<module>.<name>" */
497 	sizeof(Printer),   // int tp_basicsize, /* For allocation */
498 	0,		       // int tp_itemsize; /* For allocation */
499 
500 	/* Methods to implement standard operations */
501 
502 	(destructor) Printer_dealloc, //     destructor tp_dealloc;
503 	nullptr, //     printfunc tp_print;
504 	nullptr, //     getattrfunc tp_getattr;
505 	nullptr, //     setattrfunc tp_setattr;
506 	nullptr, //     cmpfunc tp_compare;
507 	nullptr, //     reprfunc tp_repr;
508 
509 	/* Method suites for standard classes */
510 
511 	nullptr, //     PyNumberMethods *tp_as_number;
512 	nullptr, //     PySequenceMethods *tp_as_sequence;
513 	nullptr, //     PyMappingMethods *tp_as_mapping;
514 
515 	/* More standard operations (here for binary compatibility) */
516 
517 	nullptr, //     hashfunc tp_hash;
518 	nullptr, //     ternaryfunc tp_call;
519 	nullptr, //     reprfunc tp_str;
520 	nullptr, //     getattrofunc tp_getattro;
521 	nullptr, //     setattrofunc tp_setattro;
522 
523 	/* Functions to access object as input/output buffer */
524 	nullptr, //     PyBufferProcs *tp_as_buffer;
525 
526 	/* Flags to define presence of optional/expanded features */
527 	Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,    // long tp_flags;
528 
529 	printer__doc__,      // char *tp_doc; /* Documentation string */
530 
531 	/* Assigned meaning in release 2.0 */
532 	/* call function for all accessible objects */
533 	nullptr, //     traverseproc tp_traverse;
534 
535 	/* delete references to contained objects */
536 	nullptr, //     inquiry tp_clear;
537 
538 	/* Assigned meaning in release 2.1 */
539 	/* rich comparisons */
540 	nullptr, //     richcmpfunc tp_richcompare;
541 
542 	/* weak reference enabler */
543 	0, //     long tp_weaklistoffset;
544 
545 	/* Added in release 2.2 */
546 	/* Iterators */
547 	nullptr, //     getiterfunc tp_iter;
548 	nullptr, //     iternextfunc tp_iternext;
549 
550 	/* Attribute descriptor and subclassing stuff */
551 	Printer_methods, //     struct PyMethodDef *tp_methods;
552 	Printer_members, //     struct PyMemberDef *tp_members;
553 	Printer_getseters, //     struct PyGetSetDef *tp_getset;
554 	nullptr, //     struct _typeobject *tp_base;
555 	nullptr, //     PyObject *tp_dict;
556 	nullptr, //     descrgetfunc tp_descr_get;
557 	nullptr, //     descrsetfunc tp_descr_set;
558 	0, //     long tp_dictoffset;
559 	(initproc)Printer_init, //     initproc tp_init;
560 	nullptr, //     allocfunc tp_alloc;
561 	Printer_new, //     newfunc tp_new;
562 	nullptr, //     freefunc tp_free; /* Low-level free-memory routine */
563 	nullptr, //     inquiry tp_is_gc; /* For PyObject_IS_GC */
564 	nullptr, //     PyObject *tp_bases;
565 	nullptr, //     PyObject *tp_mro; /* method resolution order */
566 	nullptr, //     PyObject *tp_cache;
567 	nullptr, //     PyObject *tp_subclasses;
568 	nullptr, //     PyObject *tp_weaklist;
569 	nullptr, //     destructor tp_del;
570 
571 #ifdef COUNT_ALLOCS
572 	/* these must be last and never explicitly initialized */
573 	//    int tp_allocs;
574 	//    int tp_frees;
575 	//    int tp_maxalloc;
576 	//    struct _typeobject *tp_next;
577 #endif
578 };
579