/*------------------------------------------------------------------------------ * Return a new list with references to the param iterator elements. * Reference elements are not destroy if the list is destroyed. * @param iter Iterator from which reference elements. * @param compare, If not NULL, use it to sort references. * @note Use an insert sort algorithm. ----------------------------------------------------------------------------*/ DLList DLList_GetRefListFromIterator(ForwardIterator iter, Container_CompareFunction compare) { DLList list = DLList_Create(); ContainerNode node = ForwardIterator_GetFirst(iter); if (compare==NULL) { while (node) { DLList_Append(list, node->elem); node = ForwardIterator_GetNext(iter); } } else { if (node) { DLList_Append(list, node->elem); node = ForwardIterator_GetNext(iter); } while (node) { DLListNode test = list->first; while (test && compare(test->elem, node->elem)) test = test->next; if (test) DLList_InsertBefore(list, test, node->elem); else DLList_Append(list, node->elem); node = ForwardIterator_GetNext(iter); } } return list; }
/*------------------------------------------------------------------------------ * Return a new list with references to the param list elements. * Reference elements are not destroy if the list is destroyed. * @param srcList Source list from which reference elements. * @param compare, If not NULL, use it to sort references. * @note Use an insert sort algorithm. ----------------------------------------------------------------------------*/ DLList DLList_GetRefList(DLList srcList, Container_CompareFunction compare) { DLList list = DLList_Create(); DLListNode node = srcList->first; if (compare == NULL) { while (node) { DLList_Append(list, node->elem); node = node->next; } } else { if (node) { DLList_Append(list, node->elem); node = node->next; } while (node) { DLListNode test = list->first; while (test && compare(test->elem, node->elem)) test = test->next; if (test) DLList_InsertBefore(list, test, node->elem); else DLList_Append(list, node->elem); node = node->next; } } return list; }
DLLEXPORT_TEST_FUNCTION void PdfGenerator_Init(struct PdfGenerator *self) { self->useCompression = FALSE; self->progressCallback = NULL; self->pdfTemplate = NULL; self->templateLoaded = 0; self->dataStreams = DLList_Create(); self->readDataCallback = NULL; self->resetDataStreamCallback = NULL; self->requestDataCallback = NULL; self->initializeDataStreamCallback = NULL; self->currentPage = NULL; self->currentContentStream = NULL; self->rectMatrix = DLList_Create(); self->pageBalloons = DLList_Create(); self->currentPageBalloon = NULL; self->pageSkipDataReadMarker = FALSE; self->pageNumber = 0; self->validSerial = FALSE; self->document = 0; }
/*---------------------------------------------------------------------- * Allocate a hash map key node (list of node). -----------------------------------------------------------------------*/ static HashMapKeyNode HashMap_CreateHashMapKeyNode(HashMap map) { HashMapKeyNode node = (HashMapKeyNode)DLList_Create(); node->destroyElement = map->destroyElement; return node; }
// This returns false when something is wrong // parentBalloon can be null. This means top level balloons should be used int PdfGenerator_GenerateBalloons(struct PdfGenerator *self, struct PdfTemplateBalloon *parentBalloon, struct PdfGeneratedBalloon *parentGeneratedBalloon, int level) { struct PdfTemplate *pdfTemplate = self->pdfTemplate; struct PdfTemplatePage *currentPageTemplate = self->pdfTemplate->page; struct DLListNode *iter; struct DLList *balloons; struct DLList *newBottomDockedBalloons; struct PdfTemplateBalloon *balloon; struct PdfGeneratorDataStream *stream; struct PdfGeneratedBalloon *newGeneratedBalloon; int tmpI = 0; int genRes; int dynamicBalloonProcessed = FALSE; char *tmpStr; short generatorResult = GENERATE_BALLOONS_OK; short generatorResultStatic = GENERATE_BALLOONS_OK; newBottomDockedBalloons = DLList_Create(); if (!parentBalloon) { Logger_LogNoticeMessage("PdfGenerator_GenerateBalloons: BEGIN NO PARENT BALLOON"); balloons = currentPageTemplate->balloons; } else { Logger_LogNoticeMessage("PdfGenerator_GenerateBalloons: BEGIN HAS PARENT BALLOON"); balloons = parentBalloon->balloons; } // 1. generate static balloons for(iter = DLList_Begin(balloons); iter != DLList_End(balloons); iter = iter->next) { balloon = (struct PdfTemplateBalloon*)iter->data; // skip balloons that are already generated and that are not available on every page if (!balloon->availableOnEveryPage && balloon->lastGeneratedPageNumber < self->pageNumber) { Logger_LogNoticeMessage("PdfGenerator_GenerateBalloons: Skip balloons - if (!balloon->availableOnEveryPage && balloon->lastGeneratedPageNumber < self->pageNumber)"); continue; } // Skip ballons that are top docked and have prev dynamic balloon top docked if (balloon->isStatic && balloon->dockPosition == DOCK_TOP && balloon->hasPrevDynamicTopDocked) { continue; } // if this is balloon on page that is bottom docked then just take place for it or // if this is balloon inside ballon that cannot grow if ((balloon->isStatic && balloon->dockPosition == DOCK_BOTTOM && parentBalloon == NULL) || (balloon->isStatic && balloon->dockPosition == DOCK_BOTTOM && balloon->parentBalloon != NULL && !balloon->parentBalloon->canGrow)) { // create it as standard static balloon but do not draw children items yet just allocate space for it newGeneratedBalloon = PdfGenerator_WriteBalloon(self, balloon, currentPageTemplate, parentGeneratedBalloon, TRUE, FALSE, TRUE); DLList_PushBack(newBottomDockedBalloons, newGeneratedBalloon); } // If this ballon is static and not docked bottom then generate it else if (balloon->isStatic && balloon->dockPosition != DOCK_BOTTOM) { // Check its data stream. If not initialized call InitializeDataStreamCallback. if failed stop stream = PdfGenerator_FindDataStream(self, balloon->dataStream); if (!balloon->skipDataReadMarker && !self->pageSkipDataReadMarker) { if (parentBalloon) { tmpStr = parentBalloon->dataStream; } else { tmpStr = NULL; } // It is ok to have no data stream to initialize for static ballons if (balloon->dataStream) { if (!PdfGenerator_InitializeDataStream(self, tmpStr, balloon->dataStream)) { Logger_LogErrorMessage("PdfGenerator_GenerateBalloons: Initializing data stream on static balloon. DataStream: %s FAILED", balloon->dataStream); return FALSE; } else { Logger_LogNoticeMessage("PdfGenerator_GenerateBalloons: Initializing data stream on static balloon. DataStream: %s SUCCESS", balloon->dataStream); } } } // Call ReadDataCallback if required if (balloon->dataStream) { if (self->readDataCallback) { if (!balloon->skipDataReadMarker && !self->pageSkipDataReadMarker) { if (!stream || (stream && !stream->initialized)) { self->readDataCallback(balloon->dataStream); Logger_LogNoticeMessage("PdfGenerator_GenerateBalloons: Read data callback for DataStream: %s SUCCESS", balloon->dataStream); } else { Logger_LogNoticeMessage("PdfGenerator_GenerateBalloons: Read data callback for DataStream %s not done. Stream Not initialized or null", balloon->dataStream); } } else { Logger_LogNoticeMessage("PdfGenerator_GenerateBalloons: Read Data not done for DataStream %s as skipDataReadMarker is true", balloon->dataStream); } } else { Logger_LogErrorMessage("PdfGenerator_GenerateBalloons: Read data callback is missing for DataStream: %s FAILED", balloon->dataStream); return FALSE; } } newGeneratedBalloon = PdfGenerator_WriteBalloon(self, balloon, currentPageTemplate, parentGeneratedBalloon, TRUE, TRUE, FALSE); // 8. Recursive call to 1. with parent balloon set as this genRes = PdfGenerator_GenerateBalloons(self, balloon, newGeneratedBalloon, level+1); // write balloon borders PdfGenerator_DrawBalloonBorders(self, balloon, newGeneratedBalloon); balloon->lastGeneratedPageNumber = self->pageNumber; if (genRes == GENERATE_BALLOONS_NOT_ENOUGH_SPACE) { Logger_LogNoticeMessage("PdfGenerator_GenerateBalloons: Static balloon returned NOT ENOUGH SPACE END"); generatorResultStatic = GENERATE_BALLOONS_NOT_ENOUGH_SPACE; } else if (genRes == GENERATE_BALLOONS_FAILED) { Logger_LogNoticeMessage("PdfGenerator_GenerateBalloons: Returned GENERATE_BALLOONS_FAILED END", balloon->dataStream); return genRes; } } } // foreach static balloon generatorResult = GENERATE_BALLOONS_OK; // 2. Generate Dynamic balloons for(iter = DLList_Begin(balloons); iter != DLList_End(balloons); iter = iter->next) { balloon = (struct PdfTemplateBalloon*)iter->data; if (!balloon->isStatic) { // 2. Check its data stream. If not initialized call InitializeDataStreamCallback. if failed stop if (!balloon->skipDataReadMarker && !self->pageSkipDataReadMarker) { if (parentBalloon) { tmpStr = parentBalloon->dataStream; } else { tmpStr = NULL; } if (!PdfGenerator_InitializeDataStream(self, tmpStr, balloon->dataStream)) { Logger_LogErrorMessage("PdfGenerator_GenerateBalloons: InitializeDataStream failed for dynamic balloon. DataStream: %s FAILED", balloon->dataStream); return FALSE; } else { Logger_LogNoticeMessage("PdfGenerator_GenerateBalloons: Initializing data stream on dynamic balloon. DataStream: %s SUCCESS", balloon->dataStream); } } stream = PdfGenerator_FindDataStream(self, balloon->dataStream); if (self->readDataCallback) { while(1) { if (!balloon->skipDataReadMarker && !self->pageSkipDataReadMarker) { if (!balloon->dataStream) { break; } if (!self->readDataCallback(balloon->dataStream)) { break; } } newGeneratedBalloon = PdfGenerator_WriteBalloon(self, balloon, currentPageTemplate, parentGeneratedBalloon, FALSE, TRUE, FALSE); if (!newGeneratedBalloon) { Logger_LogNoticeMessage("PdfGenerator_GenerateBalloons: Creating new generated balloon failed. DataStream: %s", balloon->dataStream); if (parentBalloon) { // mark parent not to move data and this balloon as we already read some data that was not written parentBalloon->skipDataReadMarker = TRUE; balloon->skipDataReadMarker = TRUE; } else { self->pageSkipDataReadMarker = TRUE; balloon->skipDataReadMarker = TRUE; } // we failed generating new balloon as not possible to write new one inside parent Generated Balloon. We need new parent. Logger_LogNoticeMessage("PdfGenerator_GenerateBalloons: DataStream: %s, Returned NOT ENOUGH SPACE END", balloon->dataStream); generatorResult = GENERATE_BALLOONS_NOT_ENOUGH_SPACE; goto GenerateBottomDocked; } else { // remove skip data markers when we generated new item correctly Logger_LogNoticeMessage("PdfGenerator_GenerateBalloons: DataStream: %s, New generated ballon created.", balloon->dataStream); balloon->skipDataReadMarker = FALSE; self->pageSkipDataReadMarker = FALSE; } // Recursive call to 1. with parent balloon set as this genRes = PdfGenerator_GenerateBalloons(self, balloon, newGeneratedBalloon, level+1); // write balloon borders PdfGenerator_DrawBalloonBorders(self, balloon, newGeneratedBalloon); balloon->lastGeneratedPageNumber = self->pageNumber; if (genRes == GENERATE_BALLOONS_NOT_ENOUGH_SPACE) { // we should continue to next iteration without checking condition and reading data Logger_LogNoticeMessage("PdfGenerator_GenerateBalloons: Not Enough Space, continue.", balloon->dataStream); continue; } else if (genRes == GENERATE_BALLOONS_FAILED) { Logger_LogNoticeMessage("PdfGenerator_GenerateBalloons: DataStream: %s, Returned GENERATE_FAILED END", balloon->dataStream); return genRes; } } } else { Logger_LogNoticeMessage("PdfGenerator_GenerateBalloons: DataStream: %s, No ReadData callback. Returned GENERATE_FAILED END", balloon->dataStream); return GENERATE_BALLOONS_FAILED; } if (stream) { stream->initialized = FALSE; } else { Logger_LogNoticeMessage("PdfGenerator_GenerateBalloons: DataStream: %s, No Stream. Returned GENERATE_FAILED END", balloon->dataStream); return GENERATE_BALLOONS_FAILED; } } } // 3. generate bottom docked static balloons // move to the last element and perform reverse until end iter = DLList_End(balloons); iter = iter->prev; for( ; iter != DLList_End(balloons) ; iter = iter->prev) { balloon = (struct PdfTemplateBalloon*)iter->data; // if balloon is static, bottom docked and its parent balloon is not null and can grow if (balloon->isStatic && balloon->dockPosition == DOCK_BOTTOM && parentBalloon != NULL && parentBalloon->canGrow) { // write new balloon but handle it as dynamic balloon newGeneratedBalloon = PdfGenerator_WriteBalloon(self, balloon, currentPageTemplate, parentGeneratedBalloon, FALSE, TRUE, TRUE); // if this balloon is not created that means we are missing it if (!newGeneratedBalloon) { Logger_LogNoticeMessage("PdfGenerator_GenerateBalloons: Creating new generated balloon failed. DataStream: %s", balloon->dataStream); if (parentBalloon) { // mark parent not to move data and this balloon as we already read some data that was not written parentBalloon->skipDataReadMarker = TRUE; balloon->skipDataReadMarker = TRUE; } else { self->pageSkipDataReadMarker = TRUE; balloon->skipDataReadMarker = TRUE; } if (stream) { // keep stream initialized as we want more items stream->initialized = TRUE; } // we failed generating new balloon as not possible to write new one inside parent Generated Balloon. We need new parent. Logger_LogNoticeMessage("PdfGenerator_GenerateBalloons: DataStream: %s, Returned NOT ENOUGH SPACE END", balloon->dataStream); generatorResult = GENERATE_BALLOONS_NOT_ENOUGH_SPACE; goto GenerateBottomDocked; } else { // remove skip data markers when we generated new item correctly Logger_LogNoticeMessage("PdfGenerator_GenerateBalloons: DataStream: %s, New generated ballon created.", balloon->dataStream); balloon->skipDataReadMarker = FALSE; self->pageSkipDataReadMarker = FALSE; } // if this newDynamicBalloon is not generated to be bottom one move it down genRes = PdfGenerator_GenerateBalloons(self, balloon, newGeneratedBalloon, level+1); PdfGenerator_DrawBalloonBorders(self, balloon, newGeneratedBalloon); balloon->lastGeneratedPageNumber = self->pageNumber; if (genRes == GENERATE_BALLOONS_NOT_ENOUGH_SPACE) { Logger_LogNoticeMessage("PdfGenerator_GenerateBalloons - Generate Bottom Docked static ones: DataStream: %s, Returned NOT ENOUGH SPACE END", balloon->dataStream); } else if (genRes == GENERATE_BALLOONS_FAILED) { Logger_LogNoticeMessage("PdfGenerator_GenerateBalloons - Generate Bottom Docked static ones: DataStream: %s, No Stream. Returned GENERATE_FAILED END", balloon->dataStream); return genRes; } } } // foreach static balloon docked bottom // 4. Generate top docked balloons for(iter = DLList_Begin(balloons); iter != DLList_End(balloons); iter = iter->next) { balloon = (struct PdfTemplateBalloon*)iter->data; // if balloon is static, bottom docked and its parent balloon is not null and can grow if (balloon->isStatic && balloon->dockPosition == DOCK_TOP && balloon->hasPrevDynamicTopDocked) { // write new balloon but handle it as dynamic balloon newGeneratedBalloon = PdfGenerator_WriteBalloon(self, balloon, currentPageTemplate, parentGeneratedBalloon, FALSE, TRUE, FALSE); // if this balloon is not created that means we are missing it if (!newGeneratedBalloon) { Logger_LogNoticeMessage("PdfGenerator_GenerateBalloons: Creating new generated balloon failed. DataStream: %s", balloon->dataStream); if (parentBalloon) { // mark parent not to move data and this balloon as we already read some data that was not written parentBalloon->skipDataReadMarker = TRUE; balloon->skipDataReadMarker = TRUE; } else { self->pageSkipDataReadMarker = TRUE; balloon->skipDataReadMarker = TRUE; } if (stream) { // keep stream initialized as we want more items stream->initialized = TRUE; } // we failed generating new balloon as not possible to write new one inside parent Generated Balloon. We need new parent. Logger_LogNoticeMessage("PdfGenerator_GenerateBalloons: DataStream: %s, Returned NOT ENOUGH SPACE END", balloon->dataStream); generatorResult = GENERATE_BALLOONS_NOT_ENOUGH_SPACE; goto GenerateBottomDocked; } else { // remove skip data markers when we generated new item correctly Logger_LogNoticeMessage("PdfGenerator_GenerateBalloons: DataStream: %s, New generated ballon created.", balloon->dataStream); balloon->skipDataReadMarker = FALSE; self->pageSkipDataReadMarker = FALSE; } // if this newDynamicBalloon is not generated to be bottom one move it down genRes = PdfGenerator_GenerateBalloons(self, balloon, newGeneratedBalloon, level+1); PdfGenerator_DrawBalloonBorders(self, balloon, newGeneratedBalloon); balloon->lastGeneratedPageNumber = self->pageNumber; if (genRes == GENERATE_BALLOONS_NOT_ENOUGH_SPACE) { Logger_LogNoticeMessage("PdfGenerator_GenerateBalloons - Generate Bottom Docked static ones: DataStream: %s, Returned NOT ENOUGH SPACE END", balloon->dataStream); } else if (genRes == GENERATE_BALLOONS_FAILED) { Logger_LogNoticeMessage("PdfGenerator_GenerateBalloons - Generate Bottom Docked static ones: DataStream: %s, No Stream. Returned GENERATE_FAILED END", balloon->dataStream); return genRes; } } } // foreach static balloon docked top GenerateBottomDocked: PdfGenerator_DrawRemainingBalloons(self, newBottomDockedBalloons); DLList_Destroy(newBottomDockedBalloons); // return not enough space if static requested more space if (generatorResultStatic == GENERATE_BALLOONS_NOT_ENOUGH_SPACE) { Logger_LogNoticeMessage("PdfGenerator_GenerateBalloons: Returned NOT ENOUGH SPACE because of static"); return generatorResultStatic; } else { if (generatorResult == GENERATE_BALLOONS_NOT_ENOUGH_SPACE) { Logger_LogNoticeMessage("PdfGenerator_GenerateBalloons: Returned NOT ENOUGH SPACE"); } else { Logger_LogNoticeMessage("PdfGenerator_GenerateBalloons: Returned OK"); } return generatorResult; } }