I am seeing issues when running PDFBox command line tool merge capability.
Both pdfbox-app-2.0.26.jar and pdfbox-app-3.0.0-SNAPSHOT.jar report the same potential issue. I am trying to merge, for example, 132 PDF files created by Edge browser from emails in HTML format. Emails in HTML format are generated by the free MBox Viewer application.
PDFBox will log more than 65,000 of warnings as below:
May 19, 2022 12:07:23 PM org.apache.pdfbox.multipdf.PDFMergerUtility appendDocument
May 19, 2022 12:07:23 PM org.apache.pdfbox.multipdf.PDFMergerUtility mergeIDTree
WARNING: key node00000001 already exists in destination IDTree
May 19, 2022 12:07:23 PM org.apache.pdfbox.multipdf.PDFMergerUtility mergeIDTree
WARNING: key node00000002 already exists in destination IDTree
May 19, 2022 12:07:23 PM org.apache.pdfbox.multipdf.PDFMergerUtility mergeIDTree
WARNING: key node00000009 already exists in destination IDTree
The generated merge file seems to be ok but it is hard to know without comparing individual PDF files with the merged file.
I would like to understand potential issues when the above warnings are logged by the PDFBox. I have cloned v3 of PDFBox from github, installed maven and built the pdfbox-app-3.0.0-SNAPSHOT.jar without any problems. The warnings are generated in the file
pdfbox\pdfbox\src\main\java\org\apache\pdfbox\multipdf\PDFMergerUtility.java by mergeIDTree() function
(see at the bottom).
I am looking for help to understand implications of the warnings since the PDFBox is a very large and complex project. During the merging process, it appears that IDs created for each PDF document are merged into a single destination container. I am not sure, but it appears that IDs represent COS objects. Each object has an associated unique key in the form of node00000001 for example. I don't know how to import the project into a visual debugger to browse and run the code. I am new to Java.
It appears that the same key may appear in different PDF documents. Not sure how keys are generated for each PDF document. Question is whether different objects end up having the same key ? Sounds like that might be the case otherwise why PDFBox would log warnings. If the collision of keys is an issue, why PDFBox would not assigned unique key ranges to each PDF document, such as node00000001-node00000100 to the first PDF document, node00000101-node00000200 to the second document and so on. I suspect that this would result in a huge IDTree structure and likely increase the size of the generated PDF, but could potentially address the warnings. This Is just my guess since I have no understanding of the merging process and I probably should not speculate.
private void mergeIDTree(PDFCloneUtility cloner,
PDStructureTreeRoot srcStructTree,
PDStructureTreeRoot destStructTree) throws IOException
{
PDNameTreeNode<PDStructureElement> srcIDTree = srcStructTree.getIDTree();
......
Map<String, PDStructureElement> srcNames = getIDTreeAsMap(srcIDTree);
Map<String, PDStructureElement> destNames = getIDTreeAsMap(destIDTree);
for (Map.Entry<String, PDStructureElement> entry : srcNames.entrySet())
{
if (destNames.containsKey(entry.getKey()))
{
LOG.warn("key " + entry.getKey() + " already exists in destination IDTree");
}
else
{
destNames.put(entry.getKey(),
new PDStructureElement((COSDictionary) cloner.cloneForNewDocument(entry.getValue().getCOSObject())));
}
}.......
Related
How do I get values of Multi-Occurrences Attribute?
I have found a partial solution here, but am not clear with getting EntryNode.
https://www.ibm.com/support/knowledgecenter/en/SSWSR9_11.6.0/com.ibm.pim.app.doc/code/pimscript/pim_ref_writevalueruleformultiatt.html
This is the part that returns always the same code:
rawCode=item.getEntryRelationshipAttrib("Product Master Catalog Spec/Raw Materials Details/Code");
That is because it always returns the first occurrence.
I think your code should look something like this:
pnode = entrynode.getEntryNodeParent();
attribCode = pnode.getEntryNode("/Code").getEntryNodeValue();
//This returns the path with the # and the correct order.
attribCodePath = pnode.getEntryNode("/Code").getEntryNodeExactPath();
rawCode=item.getEntryRelationshipAttrib(attribCodePath);
rawmatCode = rawCode[1];
var rawCatalog = getCtgByName("Raw Material Catalog") ;
rawMatItem = rawCatalog.getCtgItemByPrimaryKey(rawmatCode);
rawMatCost = rawMatItem.getEntryAttrib("Raw Material Details Spec/Cost") ;
attribQty = pnode.getEntryNode("/Qty").getEntryNodeValue();
res=attribQty*rawMatCost;
I tried to get the STM32F446ZE and the STemWin Library running in a Keil Project.
The used OS is embOS from Segger.
I tried to follow the instructions from ST to display "hello world" on the display.
I use the display driver ST7735S which should be supported by the FlexColor driver.
My problem is that instead of "hello world" the display stays white.
I configured GUIConf.h like that:
#define GUI_NUMBYTES 0x8000
In GUI_x I added the following in GUI_X_init() :
void GUI_X_Init(void) {
UC_ST7735_init();
USInt current = Sopas_usiGet_IOLink14360ST7735DisplayBacklightCurrent();
LITA_setDisplayCurrent((uint8_t)current);
}
In UC_ST7735_init() the SPI-Port gets intiated. This part should work because it ist already used in the same sensor type. The only difference is, that I removed the graphcis library we used before. The addition lines activate the display and it's backlight.
In LCDConf_FlexColor_Template.c I set the resolution like this:
#define XSIZE_PHYS 100 // To be adapted to x-screen size
#define YSIZE_PHYS 40 // To be adapted to y-screen size
The follwing three methods I filled like that:
It should be metioned that UC_ST7735_writeSPI4Wire was also used before and should work. It sends one byte after each other instead of 16 bit at once.
I tried to send the high byte and the low byte in different orders.
static void LcdWriteReg(U16 Data) {
uint8_t data[2];
UC_ST7735_dcFLAG_t dcFlag = UC_ST7735_COMMAND;
data[0] = (uint8_t)UTIL_HLP_LOW_BYTE_OF_INT16(Data);
data[1] = (uint8_t)UTIL_HLP_HIGH_BYTE_OF_INT16(Data);
UC_ST7735_writeSPI4Wire(&data[1], 1, dcFlag);
UC_ST7735_writeSPI4Wire(&data[0], 1, dcFlag);
}
static void LcdWriteData(U16 Data) {
UC_ST7735_dcFLAG_t dcFlag = UC_ST7735_DATA;
uint8_t data[2];
data[0] = (uint8_t)UTIL_HLP_LOW_BYTE_OF_INT16(Data);
data[1] = (uint8_t)UTIL_HLP_HIGH_BYTE_OF_INT16(Data);
UC_ST7735_writeSPI4Wire(&data[1], 1, dcFlag);
UC_ST7735_writeSPI4Wire(&data[0], 1, dcFlag);
}
static void LcdWriteDataMultiple(U16 * pData, int NumItems) {
UC_ST7735_dcFLAG_t dcFlag = UC_ST7735_DATA;
uint8_t data[2];
int start_size = NumItems;
while (NumItems--) {
data[0] = (uint8_t)UTIL_HLP_LOW_BYTE_OF_INT16(pData[start_size-NumItems]);
data[1] = (uint8_t)UTIL_HLP_HIGH_BYTE_OF_INT16(pData[start_size-NumItems]);
UC_ST7735_writeSPI4Wire(&data[1], 1, dcFlag);
UC_ST7735_writeSPI4Wire(&data[0], 1, dcFlag);
}
}
I didn't change LCD_X_Config()
void LCD_X_Config(void) {
GUI_DEVICE * pDevice;
CONFIG_FLEXCOLOR Config = {0};
GUI_PORT_API PortAPI = {0};
//
// Set display driver and color conversion
//
pDevice = GUI_DEVICE_CreateAndLink(GUIDRV_FLEXCOLOR, GUICC_565, 0, 0);
//
// Display driver configuration, required for Lin-driver
//
LCD_SetSizeEx (0, XSIZE_PHYS , YSIZE_PHYS);
LCD_SetVSizeEx(0, VXSIZE_PHYS, VYSIZE_PHYS);
//
// Orientation
//
Config.Orientation = GUI_SWAP_XY | GUI_MIRROR_Y;
GUIDRV_FlexColor_Config(pDevice, &Config);
//
// Set controller and operation mode
//
PortAPI.pfWrite16_A0 = LcdWriteReg;
PortAPI.pfWrite16_A1 = LcdWriteData;
PortAPI.pfWriteM16_A1 = LcdWriteDataMultiple;
PortAPI.pfReadM16_A1 = LcdReadDataMultiple;
GUIDRV_FlexColor_SetFunc(pDevice, &PortAPI, GUIDRV_FLEXCOLOR_F66708, GUIDRV_FLEXCOLOR_M16C0B16);
}
The code I run is:
GUI_Init();
int xPos, yPos;
//__HAL_RCC_CRC_CLK_ENABLE();
xPos = LCD_GetXSize() / 2;
yPos = LCD_GetYSize() / 3;
GUI_SetFont(GUI_FONT_COMIC24B_ASCII);
GUI_DispStringHCenterAt("Hello world!", xPos, yPos);
// Endless loop:
while(true)
{
OS_TASK_Delay(100);
MW_TWD_arm(); // Arm the watchdog
}
There is no Compile-Error and the Watch Dog does not cause a problem.
The problem is that the display stays white.
I noticed that the command number which the flexchain driver sends over spi does not match the excpeted numbers from the St7735 datasheet.
I debugged the SPI method. The follwing commands and data got sent.
a command with 0x00 as the high byte
a command with 0x03 as the low byte. As far I know there is no command number 3 for theSTt7735
data with 0x00 as the high byte
data with 0x00 as the low byte
a command with 0x00 as the high byte
a command with 0x50 ('P') as the low byte.
data with 0x00 as the high byte
data with 0x00 as the low byte
a command with 0x00 as the high byte
a command with 0x51 ('Q') as the low byte.
data with 0x00 as the high byte
data with 0x63 as the low byte
a command with 0x00 as the high byte
a command with 0x52 as the low byte.
data with 0x00 as the high byte
data with 0x00 as the low byte ...
after a while with more alternating commands and data there is just data with 0x00
Did I forget to config something?
Update:
Changing GUIDRV_FLEXCOLOR_F66708 to GUIDRV_FLEXCOLOR_F667089 helped in order to get the right command numbers for the ST7735 display driver.
Changing GUIDRV_FLEXCOLOR_M16C0B16 to GUIDRV_FLEXCOLOR_M16C1B8 doesn't. My device reboots before any SPI communication happens.
I debugged the program to known when The error occurs.
I runs the following methods without crashing but after the last one I didn't see an other name of a method in the disassmbly window of Keil.
emwin_LCD_init
LCD_SETBk_ColorIndex
LCD_set_ClipRectMax
GUI_Alloc_getFixedBlock
GUI_Device_GetpDriver
GUIDRV_Flexcolor_InitOnce
GUI_Alloc_getFixedBlock
LCD_X_DisplayDriver
It get's to the end of LCD_X_DisplayDriver but never to LcdWriteReg, LcdWriteData, LcdWriteDataMultiple or LcdReadDataMultiple.
What could be the problem?
I have a method in which a data sheet is committed.
private void saveItemsToDB(List<NewsEntity> newsEntityList) {
Disposable disposable = Completable.fromCallable((Callable<Void>) () -> {
newsDatabase.getNewsDao().deleteAll();
Utils.log("******Delete All******");
for (NewsEntity newsEntity : newsEntityList) {
Utils.log("******Save " + newsEntity + "******");
newsDatabase.getNewsDao().insert(newsEntity);
}
return null;
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe();
compositeDisposable.add(disposable);
}
I need to save them to the database. I work with Room. There is a method
#Insert(onConflict = OnConflictStrategy.REPLACE)
void insertAll(NewsEntity... newsEntities);
#Insert(onConflict = OnConflictStrategy.REPLACE)
void insert(NewsEntity newsEntity);
No saving occurs Link
According to RxJava2 documentation, it's not allowed to pass null through rx chain. I suppose, it's the cause of the problem.
Completable.fromCallable((Callable<Void>) () -> {
...
return null; // don't do this
})
If you don't want to return anything, use Completable.fromAction()
The system I set up does the following:
a user submits a form
a confirmation email is sent with a link to edit the response (so far so good)
in the form, there is a 'due date' question
a trigger script scans these dates each day
when the due date has come, it sends a reminder email (this also happens properly, but)
in this reminder email, the edit link is repeated, so that the user doesn't have to search for the previous mail
Sadly this link is sent out wrong. Instead of linking to the proper response, it gives the link to the response that has been edited (submitted) last.
Here is the script:
function sendReminderEmail() {
var sheet = SpreadsheetApp.getActiveSheet();
var startRow = 2; // First row of data to process
var numRows = sheet.getLastRow()-1; // Number of rows to process
// Fetch the range of cells A2:B3
var dataRange = sheet.getRange(startRow, 1, numRows, sheet.getLastColumn());
// Fetch values for each row in the Range.
var data = dataRange.getValues();
//Logger.log(data)
var form = FormApp.openById("IDremovedByMe");
var formResponses = form.getResponses();
var r = formResponses.length-1;
var editURL = formResponses[r].getEditResponseUrl();
//Get the Edit URL
for (i in data) {
var row = data[i];
var date = new Date();
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
//Logger.log(date);
var sheetDate = new Date(row[13]);
//Logger.log(sheetDate);
var Sdate = Utilities.formatDate(date,'GMT+0100','yyyy:MM:dd')
var SsheetDate = Utilities.formatDate(sheetDate,'GMT+0100', 'yyyy:MM:dd')
Logger.log(Sdate+' =? '+SsheetDate)
if (Sdate == SsheetDate){
var sendTo = row[4]; // Collect email address from the fourth column (number value is always -1).
var sendMe = "xyzabcloremipsum#gmail.com"; // Enter the email address where you wish to receive a notification about a reminder sent.
var sendername = "Auto Formbot"; // Name displayed as the sender.
var myname = "Formbot"; // Name displayed as the sender to you.
var messageTo = "Based on the data you entered, the '" +row[6] +"' project with the ID: " +row[1] +" has ended.\n\nPlease mark it as 'Completed' and update the details as soon as it's convenient for you.\n\nYou can edit your data by using the following link:\n" + editURL + "\n\nThank you for your time.\n\n\nWith kind regards,\nFormbot";
var messageMe = "The '" +row[6] +"' project with the ID: " +row[1] +" has finished today.\n\nA reminder email has been sent to " +row[4] +".\n\nYou can edit the data by using the following link:\n" + editURL + "\n\n\nSincerely,\nFormbot";
// Above is the column (number value is always -1) selected for activity name display.
var subjectTo = "Please update the '" +row[6] +"' activity data.";
var subjectMe = "An activity has finished today [ID: " +row[1] +"].";
MailApp.sendEmail(sendTo, subjectTo, messageTo, {name: sendername});
MailApp.sendEmail(sendMe, subjectMe, messageMe, {name: myname});
}
}
}
The problem is obviously in this part:
var form = FormApp.openById("IDremovedByMe");
var formResponses = form.getResponses();
var r = formResponses.length-1;
var editURL = formResponses[r].getEditResponseUrl();
I'm just not sure how to explain the script how to get the appropriate response.
Perhaps my approach is wrong, maybe I should tell the script to scan the form database instead the linked spreadsheet? Any ideas on how to do that?
So, I chose to insert the URLs directly into the responses sheet and reference them from there.
I used the script found here.
function injectEditURLs() {
// Form ID:
var form = FormApp.openById('IDremovedByMe');
// Name of the (main) sheet and NOT the Sheet file name where the URLs will appear:
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Responses');
var data = sheet.getDataRange().getValues();
var urlCol = 11; // The number of the column in which the URL will be inserted; A = 1, B = 2 etc.
var responses = form.getResponses();
var timestamps = [], urls = [], resultUrls = [];
for (var i = 0; i < responses.length; i++) {
timestamps.push(responses[i].getTimestamp().setMilliseconds(0));
urls.push(responses[i].getEditResponseUrl());
}
for (var j = 1; j < data.length; j++) {
resultUrls.push([data[j][0]?urls[timestamps.indexOf(data[j][0].setMilliseconds(0))]:'']);
}
sheet.getRange(2, urlCol, resultUrls.length).setValues(resultUrls);
}
Then I just referenced it (and removed the unnecessary bits) in the reminder email script with:
" + row[n] +"
So it now looks and works something like this:
function sendReminderEmail() {
var sheet = SpreadsheetApp.getActiveSheet();
var startRow = 2; // First row of data to process
var numRows = sheet.getLastRow()-1; // Number of rows to process
// Fetch the range of cells A2:B3
var dataRange = sheet.getRange(startRow, 1, numRows, sheet.getLastColumn());
// Fetch values for each row in the Range.
var data = dataRange.getValues();
//Logger.log(data)
for (i in data) {
var row = data[i];
var date = new Date();
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
//Logger.log(date);
var sheetDate = new Date(row[13]);
//Logger.log(sheetDate);
var Sdate = Utilities.formatDate(date,'GMT+0100','yyyy:MM:dd')
var SsheetDate = Utilities.formatDate(sheetDate,'GMT+0100', 'yyyy:MM:dd')
Logger.log(Sdate+' =? '+SsheetDate)
if (Sdate == SsheetDate){
var sendTo = row[4]; // Collect email address from the fourth column (number value is always -1).
var sendMe = "xyzabcloremipsum#gmail.com"; // Enter the email address where you wish to receive a notification about a reminder sent.
var sendername = "Auto Formbot"; // Name displayed as the sender.
var myname = "Formbot"; // Name displayed as the sender to you.
var messageTo = "Based on the data you entered, the '" +row[6] +"' project with the ID: " +row[1] +" has ended.\n\nPlease mark it as 'Completed' and update the details as soon as it's convenient for you.\n\nYou can edit your data by using the following link:\n" + row[10] + "\n\nThank you for your time.\n\n\nWith kind regards,\nFormbot";
var messageMe = "The '" +row[6] +"' project with the ID: " +row[1] +" has finished today.\n\nA reminder email has been sent to " +row[4] +".\n\nYou can edit the data by using the following link:\n" + row[10] + "\n\n\nSincerely,\nFormbot";
// Above is the column (number value is always -1 because A=0) selected for activity name display.
var subjectTo = "Please update the '" +row[6] +"' activity data.";
var subjectMe = "An activity has finished today [ID: " +row[1] +"].";
MailApp.sendEmail(sendTo, subjectTo, messageTo, {name: sendername});
MailApp.sendEmail(sendMe, subjectMe, messageMe, {name: myname});
}
}
}
Your own idea about scanning from the form database is most viable option. Here is code snippet, from Google form documentation page that does exactly that :
// Open a form by ID and log the responses to each question.
var form = FormApp.openById('1234567890abcdefghijklmnopqrstuvwxyz');
var formResponses = form.getResponses();
for (var i = 0; i < formResponses.length; i++) {
var formResponse = formResponses[i];
var itemResponses = formResponse.getItemResponses();
for (var j = 0; j < itemResponses.length; j++) {
var itemResponse = itemResponses[j];
Logger.log('Response #%s to the question "%s" was "%s"',
(i + 1).toString(),
itemResponse.getItem().getTitle(),
itemResponse.getResponse());
}
}
Here is the link to that page: https://developers.google.com/apps-script/reference/forms/item-response
Basically, you will get the form responses using form.responses() then loop through each response and get the due date and check to see the due date is same as today. Then send the edit url for that particular response.
Hope this helps, all the best
The following code doesn't work for device Its not going into the while loop on device but it runs on the simulator.
int status;
char value[1024] = "abcd";
FILE *fp = popen("openssl enc -aes-128-cbc -k secret -P -md sha1 2>&1", "r");
if (fp == NULL)
exit(1); // handle error
int i=0;
NSString *strAESKey;
while (fgets(value, 1024, fp) != NULL)
{
i++;
if(i==2)
{
strAESKey=[NSString stringWithFormat:#"%s",value];
break;
}
}
status = pclose(fp);
if (status == -1)
{
/* Error reported by pclose() */
}
else
{
/* Use macros described under wait() to inspect `status' in order
to determine success/failure of command executed by popen() */
}
Where am I going wrong?
The iOS application sandbox forbids use of the fork function, which popen uses. The simulator doesn't use the sandbox, but devices do.
You will need to use the openssl library directly instead of using the command-line program. The iOS public API doesn't include the openssl library, so you'll need to build a static library yourself. You can find some help doing this by searching. I'd start with this blog post.